Skip to content

Commit e4095e4

Browse files
committed
Improve semantic tone classes guide.
1 parent c02a55d commit e4095e4

5 files changed

Lines changed: 678 additions & 248 deletions

File tree

‎frontend/docs/componentGuide.ts‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,34 @@ export function registerComponentGuideStyles() {
5959
const root = styleBuilder.get(theme)
6060
const neutral = palette.semanticTone.neutral
6161
const accent = palette.semanticTone.accent
62+
root.select('.tone-guide-swatch').css({ minHeight: '7rem', width: '100%' })
63+
root.select(':where(.tone-guide-state)').css({
64+
background: 'transparent',
65+
border: '0 solid transparent',
66+
color: 'inherit',
67+
cursor: 'pointer',
68+
})
69+
root
70+
.select('.tone-guide-state')
71+
.css({ font: 'inherit', textAlign: 'start' })
72+
root.select('.tone-guide-flow').css({
73+
display: 'grid',
74+
gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 10rem), 1fr))',
75+
gap: '0.75rem',
76+
})
77+
root.select('.tone-guide-flow > div').css({
78+
padding: '1rem',
79+
border: `1px solid ${accent.border.subtle}`,
80+
borderRadius: '0.75rem',
81+
background: accent.surface.rest.background,
82+
minWidth: '0',
83+
})
84+
root
85+
.select('.tone-guide-flow code, .tone-guide-gallery code')
86+
.css({ overflowWrap: 'anywhere', whiteSpace: 'normal' })
87+
root
88+
.select('.tone-guide-glass-field')
89+
.css({ backgroundColor: accent.spotlight.field })
6290
root.select('.runtime-code').css({
6391
whiteSpace: 'pre-wrap',
6492
overflowWrap: 'anywhere',

‎frontend/docs/docsComponents.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { defineChartAppearanceGallery } from './dataGuide'
1010
import { defineFormAppearanceGallery } from './formAppearance'
1111
import { defineLandingAppearanceGallery } from './landingGuide'
1212
import { defineModalExampleComponents } from './modal'
13+
import { defineSemanticToneGallery } from './semanticToneGuide'
1314
import { defineSiteAppearanceGallery } from './siteGuide'
1415

1516
export function defineDocumentationComponents(site: SiteConfig) {
@@ -19,6 +20,7 @@ export function defineDocumentationComponents(site: SiteConfig) {
1920
ChartAppearanceGallery: defineChartAppearanceGallery(),
2021
LandingAppearanceGallery: defineLandingAppearanceGallery(),
2122
SiteAppearanceGallery: defineSiteAppearanceGallery(site),
23+
SemanticToneGallery: defineSemanticToneGallery(),
2224
ApiProperty: defineApiPropertyComponent(),
2325
ConsentPreview: defineConsentPreviewComponent(site),
2426
NavMenuPreview: defineNavMenuPreviewComponent(site),

‎frontend/docs/semanticToneGuide.ts‎

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import type { ComponentVariant } from '@purestack/ts-components'
2+
import { getCurrentThemePalette, SEMANTIC_TONES } from '@purestack/ts-style'
3+
import { defineComponent, html, type RefOrValue, unref } from 'regor'
4+
import { resolveComponentClasses } from '../../packages/ts-components/src/standard/componentVariant'
5+
6+
const current = getCurrentThemePalette()
7+
const purposes = [
8+
'Everyday structure',
9+
'Brand emphasis',
10+
'Featured capability',
11+
'Secondary emphasis',
12+
'Skin-defined meaning',
13+
'Restrained context',
14+
'Helpful information',
15+
'Positive confirmation',
16+
'Needs attention',
17+
'Critical condition',
18+
]
19+
const fills = [
20+
'tone-fill',
21+
'tone-fill-canvas',
22+
'tone-fill-surface',
23+
'tone-fill-surface-alt',
24+
'tone-fill-button',
25+
'tone-fill-flat',
26+
'tone-fill-flat-alt',
27+
'tone-fill-flat-solid',
28+
'tone-fill-spotlight',
29+
'tone-fill-glass',
30+
]
31+
const variants: ComponentVariant[] = [
32+
'solid',
33+
'surface',
34+
'surfaceAlt',
35+
'spotlight',
36+
'glass',
37+
'flat',
38+
'flatAlt',
39+
'flatSolid',
40+
'outlineFill',
41+
'outline',
42+
'subtle',
43+
'subtleBtn',
44+
'link',
45+
'sheen',
46+
'underline',
47+
'rail',
48+
'bracket',
49+
'none',
50+
]
51+
52+
export function defineSemanticToneGallery() {
53+
return defineComponent<{ axis: RefOrValue<string> }>(
54+
html`<div class="component-appearance-grid tone-guide-gallery">
55+
<article class="component-appearance-cell" r-for="sample in samples" :data-tone-sample="sample.label">
56+
<code>{{ sample.label }}</code>
57+
<div r-if="axis === 'tones'" :class="sample.classes + ' rounded-md p-3'">
58+
<strong>{{ sample.description }}</strong><p class="fs-sm mb-2">One palette. Several useful roles.</p>
59+
<div class="tone-fill-surface-alt tone-text-surface-alt tone-border-surface-alt b-1 rounded-md p-2 mb-2">Alternate surface</div>
60+
<span class="d-inline-block tone-fill-button tone-text-button tone-border-button b-1 rounded-md p-2">Button role</span>
61+
</div>
62+
<div r-if="axis === 'fills'" :class="sample.classes + ' tone-guide-swatch rounded-md'"></div>
63+
<span r-if="axis === 'fills'" class="fs-sm">{{ sample.description }}</span>
64+
<div r-if="axis === 'insets' || axis === 'sizes'" :class="sample.classes + ' tone-guide-swatch rounded-md p-3'">Inset accent</div>
65+
<div r-if="axis === 'origins'" :class="sample.classes + ' rounded-md p-3 tone-guide-swatch'"><strong>Shared light</strong><div class="tone-fill-glass tone-border-surface-alt b-1 rounded-md p-3 mt-2">Glass follows the same origin</div></div>
66+
<div r-if="axis === 'states' || axis === 'variants'" class="tone--accent"><button type="button" :class="sample.classes + ' tone-guide-state p-3 w-full'" :disabled="sample.disabled" :style="sample.style">{{ sample.description }}</button></div>
67+
<p r-if="sample.note" class="fs-sm m-0">{{ sample.note }}</p>
68+
</article>
69+
</div>`,
70+
{
71+
props: ['axis'],
72+
context: (head) => {
73+
const axis = unref(head.props.axis)
74+
let samples: Array<{
75+
label: string
76+
classes: string
77+
description?: string
78+
disabled?: boolean
79+
style?: object
80+
note?: string
81+
}> = []
82+
if (axis === 'variants')
83+
samples = variants.map((variant) => ({
84+
label: variant,
85+
classes: resolveComponentClasses({
86+
tone: 'accent',
87+
variant,
88+
variantMode: 'stateful',
89+
}),
90+
description: variant,
91+
}))
92+
if (axis === 'tones')
93+
samples = SEMANTIC_TONES.map((tone, i) => ({
94+
label: `tone--${tone}`,
95+
classes: `tone--${tone} tone-fill-surface tone-border-surface tone-text-surface b-1`,
96+
description: purposes[i],
97+
}))
98+
if (axis === 'fills')
99+
samples = fills.map((fill, i) => ({
100+
label: fill,
101+
classes: `tone--accent ${fill}${fill === 'tone-fill-glass' ? ' tone-guide-glass-field' : ''}`,
102+
description: [
103+
'Direct tone color',
104+
'Canvas background',
105+
'Surface background',
106+
'Alternate surface background',
107+
'Button background',
108+
'Surface bgcolor only',
109+
'Alternate surface bgcolor only',
110+
'Button bgcolor only',
111+
'Field + radial light',
112+
'Light + translucent veil',
113+
][i],
114+
}))
115+
if (axis === 'insets')
116+
samples = ['l', 'r', 't', 'b', 'x', 'y'].map((direction) => ({
117+
label: `tone-inset-${direction}`,
118+
classes: `tone--feature tone-fill-surface tone-text-surface tone-inset-${direction} inset-size-2`,
119+
}))
120+
if (axis === 'sizes')
121+
samples = [0, 1, 2, 3, 4, 5, 6].map((size) => ({
122+
label: `inset-size-${size}`,
123+
classes: `tone--info tone-fill-surface tone-text-surface tone-inset-l inset-size-${size}`,
124+
}))
125+
if (axis === 'origins')
126+
samples = [
127+
'top-left',
128+
'top',
129+
'top-right',
130+
'bottom-left',
131+
'bottom-right',
132+
].map((origin) => ({
133+
label: `spotlight-from-${origin}`,
134+
classes: `tone--accent tone-fill-spotlight tone-text-surface-alt spotlight-from-${origin}`,
135+
}))
136+
if (axis === 'states')
137+
samples = (['surface', 'surfaceAlt', 'button'] as const).flatMap(
138+
(role) =>
139+
(['rest', 'hover', 'active', 'disabled'] as const).map(
140+
(state) => {
141+
const suffix = role === 'surfaceAlt' ? 'surface-alt' : role
142+
const token = current[role][state]
143+
return {
144+
label: `${suffix} · ${state}`,
145+
classes: `b-1 rounded-md tone-fill-${suffix}-all tone-border-${suffix}-all tone-text-${suffix}-all${state === 'active' ? ' active' : ''}`,
146+
description:
147+
state === 'hover'
148+
? 'Hover token snapshot'
149+
: state === 'active'
150+
? 'Persistent .active class'
151+
: state === 'disabled'
152+
? 'Native disabled button'
153+
: 'Rest · hover or press me',
154+
disabled: state === 'disabled',
155+
style:
156+
state === 'hover'
157+
? {
158+
background: token.background,
159+
borderColor: token.border,
160+
color: token.text,
161+
}
162+
: undefined,
163+
note:
164+
state === 'hover'
165+
? 'The hover token is painted directly so the state remains visible without a pointer.'
166+
: undefined,
167+
}
168+
},
169+
),
170+
)
171+
return { ...head.props, samples }
172+
},
173+
},
174+
)
175+
}

0 commit comments

Comments
 (0)