@adea-ai/ui 0.104.3 → 0.106.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -8
- package/dist/components/composites/action-button/action-button.d.ts.map +1 -1
- package/dist/components/composites/action-button/action-button.js +6 -8
- package/dist/components/composites/action-button/action-button.js.map +1 -1
- package/dist/components/composites/appearance-editor/accent-swatch-groups.d.ts +16 -0
- package/dist/components/composites/appearance-editor/accent-swatch-groups.d.ts.map +1 -0
- package/dist/components/composites/appearance-editor/accent-swatch-groups.js +151 -0
- package/dist/components/composites/appearance-editor/accent-swatch-groups.js.map +1 -0
- package/dist/components/composites/appearance-editor/appearance-choices.d.ts.map +1 -1
- package/dist/components/composites/appearance-editor/appearance-choices.js +24 -84
- package/dist/components/composites/appearance-editor/appearance-choices.js.map +1 -1
- package/dist/components/composites/appearance-editor/appearance-editor.d.ts +2 -2
- package/dist/components/composites/appearance-editor/appearance-editor.d.ts.map +1 -1
- package/dist/components/composites/appearance-editor/appearance-editor.js +25 -7
- package/dist/components/composites/appearance-editor/appearance-editor.js.map +1 -1
- package/dist/components/composites/appearance-editor/appearance-types.d.ts +9 -0
- package/dist/components/composites/appearance-editor/appearance-types.d.ts.map +1 -1
- package/dist/components/composites/appearance-editor/font-settings-group.d.ts +10 -0
- package/dist/components/composites/appearance-editor/font-settings-group.d.ts.map +1 -0
- package/dist/components/composites/appearance-editor/font-settings-group.js +310 -0
- package/dist/components/composites/appearance-editor/font-settings-group.js.map +1 -0
- package/dist/components/composites/appearance-editor/index.d.ts +2 -0
- package/dist/components/composites/appearance-editor/index.d.ts.map +1 -1
- package/dist/components/composites/appearance-editor/index.js +3 -1
- package/dist/components/composites/update-dialog/update-dialog.js +2 -2
- package/dist/components/composites/update-dialog/update-dialog.js.map +1 -1
- package/dist/components/conversation/message-row.d.ts.map +1 -1
- package/dist/components/conversation/message-row.js +3 -3
- package/dist/components/conversation/message-row.js.map +1 -1
- package/dist/components/conversation/paste-token-editor.js +1 -1
- package/dist/components/conversation/paste-token-editor.js.map +1 -1
- package/dist/components/layout/side-rail/side-rail.js +2 -2
- package/dist/components/layout/side-rail/side-rail.js.map +1 -1
- package/dist/components/layout/top-bar/top-bar.js +1 -1
- package/dist/components/layout/top-bar/top-bar.js.map +1 -1
- package/dist/components/theme/appearance-panel.d.ts +1 -1
- package/dist/components/theme/appearance-panel.d.ts.map +1 -1
- package/dist/components/theme/appearance-panel.js +45 -105
- package/dist/components/theme/appearance-panel.js.map +1 -1
- package/dist/components/theme/theme-provider.d.ts +5 -2
- package/dist/components/theme/theme-provider.d.ts.map +1 -1
- package/dist/components/theme/theme-provider.js +38 -19
- package/dist/components/theme/theme-provider.js.map +1 -1
- package/dist/components/ui/code-block/code-block.d.ts +3 -3
- package/dist/components/ui/code-block/code-block.js +6 -6
- package/dist/components/ui/code-block/code-block.js.map +1 -1
- package/dist/components/ui/command/command.js +1 -1
- package/dist/components/ui/command/command.js.map +1 -1
- package/dist/components/ui/context-menu/context-menu.js +1 -1
- package/dist/components/ui/context-menu/context-menu.js.map +1 -1
- package/dist/components/ui/diff-block/diff-block.js +5 -5
- package/dist/components/ui/diff-block/diff-block.js.map +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu.js +101 -8
- package/dist/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
- package/dist/components/ui/kbd/index.d.ts +1 -1
- package/dist/components/ui/kbd/index.d.ts.map +1 -1
- package/dist/components/ui/kbd/index.js +2 -2
- package/dist/components/ui/kbd/kbd.d.ts +40 -2
- package/dist/components/ui/kbd/kbd.d.ts.map +1 -1
- package/dist/components/ui/kbd/kbd.js +74 -7
- package/dist/components/ui/kbd/kbd.js.map +1 -1
- package/dist/components/ui/menubar/menubar.js +1 -1
- package/dist/components/ui/menubar/menubar.js.map +1 -1
- package/dist/components/ui/typography/typography.d.ts +3 -5
- package/dist/components/ui/typography/typography.d.ts.map +1 -1
- package/dist/components/ui/typography/typography.js +6 -8
- package/dist/components/ui/typography/typography.js.map +1 -1
- package/dist/index.js +7 -4
- package/dist/lib/appearance-font-settings.d.ts +53 -0
- package/dist/lib/appearance-font-settings.d.ts.map +1 -0
- package/dist/lib/appearance-font-settings.js +160 -0
- package/dist/lib/appearance-font-settings.js.map +1 -0
- package/dist/lib/font-catalog.d.ts +72 -0
- package/dist/lib/font-catalog.d.ts.map +1 -0
- package/dist/lib/font-catalog.js +71 -0
- package/dist/lib/font-catalog.js.map +1 -0
- package/dist/lib/safe-script.d.ts +3 -0
- package/dist/lib/safe-script.d.ts.map +1 -0
- package/dist/lib/safe-script.js +9 -0
- package/dist/lib/safe-script.js.map +1 -0
- package/dist/lib/tokens.d.ts +2 -19
- package/dist/lib/tokens.d.ts.map +1 -1
- package/dist/lib/tokens.js +63 -39
- package/dist/lib/tokens.js.map +1 -1
- package/dist/r/about-dialog.json +1 -1
- package/dist/r/accordion.json +1 -1
- package/dist/r/account-menu.json +1 -1
- package/dist/r/alert.json +1 -1
- package/dist/r/appearance-editor.json +13 -2
- package/dist/r/breadcrumb.json +1 -1
- package/dist/r/calendar.json +1 -1
- package/dist/r/carousel.json +1 -1
- package/dist/r/catalog-browser.json +1 -1
- package/dist/r/checkbox.json +1 -1
- package/dist/r/code-block.json +1 -1
- package/dist/r/combobox.json +1 -1
- package/dist/r/command.json +1 -1
- package/dist/r/context-menu.json +1 -1
- package/dist/r/conversation.json +1 -1
- package/dist/r/detail-panel.json +1 -1
- package/dist/r/dialog.json +1 -1
- package/dist/r/dropdown-menu.json +1 -1
- package/dist/r/empty.json +1 -1
- package/dist/r/floating-preview.json +1 -1
- package/dist/r/help-center.json +1 -1
- package/dist/r/lib.json +16 -1
- package/dist/r/menubar.json +1 -1
- package/dist/r/native-select.json +1 -1
- package/dist/r/navigation-menu.json +1 -1
- package/dist/r/pagination.json +1 -1
- package/dist/r/radio-group.json +1 -1
- package/dist/r/registry.json +75 -42
- package/dist/r/resizable.json +1 -1
- package/dist/r/scene-controls.json +1 -1
- package/dist/r/select.json +1 -1
- package/dist/r/sheet.json +1 -1
- package/dist/r/sidebar-nav.json +1 -1
- package/dist/r/spinner.json +1 -1
- package/dist/r/split-layout.json +1 -1
- package/dist/r/src/components/composites/action-button/action-button.tsx +2 -1
- package/dist/r/src/components/composites/appearance-editor/accent-swatch-groups.tsx +147 -0
- package/dist/r/src/components/composites/appearance-editor/appearance-choices.tsx +17 -74
- package/dist/r/src/components/composites/appearance-editor/appearance-editor.tsx +19 -6
- package/dist/r/src/components/composites/appearance-editor/appearance-types.ts +9 -0
- package/dist/r/src/components/composites/appearance-editor/font-settings-group.tsx +350 -0
- package/dist/r/src/components/composites/appearance-editor/index.ts +19 -0
- package/dist/r/src/components/composites/update-dialog/update-dialog.tsx +2 -2
- package/dist/r/src/components/conversation/message-row.tsx +7 -3
- package/dist/r/src/components/conversation/paste-token-editor.tsx +1 -1
- package/dist/r/src/components/layout/side-rail/side-rail.tsx +3 -3
- package/dist/r/src/components/layout/top-bar/top-bar.tsx +1 -1
- package/dist/r/src/components/theme/appearance-panel.tsx +34 -83
- package/dist/r/src/components/theme/theme-provider.tsx +50 -20
- package/dist/r/src/components/ui/code-block/code-block.tsx +6 -6
- package/dist/r/src/components/ui/command/command.tsx +1 -1
- package/dist/r/src/components/ui/context-menu/context-menu.tsx +1 -1
- package/dist/r/src/components/ui/diff-block/diff-block.tsx +7 -7
- package/dist/r/src/components/ui/dropdown-menu/dropdown-menu.tsx +145 -7
- package/dist/r/src/components/ui/kbd/index.ts +10 -1
- package/dist/r/src/components/ui/kbd/kbd.tsx +77 -14
- package/dist/r/src/components/ui/menubar/menubar.tsx +1 -1
- package/dist/r/src/components/ui/typography/typography.tsx +6 -8
- package/dist/r/src/lib/appearance-font-settings.ts +233 -0
- package/dist/r/src/lib/font-catalog.ts +80 -0
- package/dist/r/src/lib/safe-script.ts +7 -0
- package/dist/r/src/lib/tokens.ts +64 -59
- package/dist/r/src/styles/appearance-font-settings.css +110 -0
- package/dist/r/src/styles/fonts.css +4 -5
- package/dist/r/src/styles/globals.css +4 -1
- package/dist/r/src/styles/theme.css +14 -0
- package/dist/r/stat.json +1 -1
- package/dist/r/theme.json +5 -0
- package/dist/r/theming.json +4 -2
- package/dist/r/toast.json +1 -1
- package/dist/r/top-bar.json +1 -1
- package/dist/r/tree.json +1 -1
- package/dist/r/update-dialog.json +1 -1
- package/package.json +7 -6
- package/registry.json +75 -42
- package/src/components/composites/action-button/action-button.tsx +2 -1
- package/src/components/composites/appearance-editor/accent-swatch-groups.tsx +147 -0
- package/src/components/composites/appearance-editor/appearance-choices.tsx +17 -74
- package/src/components/composites/appearance-editor/appearance-editor.tsx +19 -6
- package/src/components/composites/appearance-editor/appearance-types.ts +9 -0
- package/src/components/composites/appearance-editor/font-settings-group.tsx +350 -0
- package/src/components/composites/appearance-editor/index.ts +19 -0
- package/src/components/composites/update-dialog/update-dialog.tsx +2 -2
- package/src/components/conversation/message-row.tsx +7 -3
- package/src/components/conversation/paste-token-editor.tsx +1 -1
- package/src/components/layout/side-rail/side-rail.tsx +3 -3
- package/src/components/layout/top-bar/top-bar.tsx +1 -1
- package/src/components/theme/appearance-panel.tsx +34 -83
- package/src/components/theme/theme-provider.tsx +50 -20
- package/src/components/ui/code-block/code-block.tsx +6 -6
- package/src/components/ui/command/command.tsx +1 -1
- package/src/components/ui/context-menu/context-menu.tsx +1 -1
- package/src/components/ui/diff-block/diff-block.tsx +7 -7
- package/src/components/ui/dropdown-menu/dropdown-menu.tsx +145 -7
- package/src/components/ui/kbd/index.ts +10 -1
- package/src/components/ui/kbd/kbd.tsx +77 -14
- package/src/components/ui/menubar/menubar.tsx +1 -1
- package/src/components/ui/typography/typography.tsx +6 -8
- package/src/lib/appearance-font-settings.ts +233 -0
- package/src/lib/font-catalog.ts +80 -0
- package/src/lib/safe-script.ts +7 -0
- package/src/lib/tokens.ts +64 -59
- package/src/styles/appearance-font-settings.css +110 -0
- package/src/styles/fonts.css +4 -5
- package/src/styles/globals.css +4 -1
- package/src/styles/theme.css +14 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ComponentProps } from 'solid-js'
|
|
2
|
-
import { splitProps } from 'solid-js'
|
|
2
|
+
import { For, splitProps } from 'solid-js'
|
|
3
|
+
import { cva, type VariantProps } from '#lib/variants'
|
|
3
4
|
import { cn } from '#lib/utils'
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -9,32 +10,94 @@ import { cn } from '#lib/utils'
|
|
|
9
10
|
* `<kbd>` so the semantics survive copy-paste, and the mono face is what makes
|
|
10
11
|
* a shortcut visually distinct from the sentence around it.
|
|
11
12
|
*
|
|
13
|
+
* `size="compact"` exists for the dense rows a default cap would outweigh — a
|
|
14
|
+
* rail search row, a sidebar shortcut hint. It drops a height rung, and it
|
|
15
|
+
* lightens the fill and weight with it because at that size a solid muted cap
|
|
16
|
+
* reads heavier than the row text it sits in. The default rendering is every
|
|
17
|
+
* cap that is not inside such a row, and it is untouched by the rung.
|
|
18
|
+
*
|
|
12
19
|
* `KbdGroup` exists because a shortcut is a sequence: typing `⌘` then `K`
|
|
13
20
|
* needs a gap between them that is a layout concern, not punctuation.
|
|
14
21
|
*/
|
|
15
|
-
export
|
|
16
|
-
|
|
22
|
+
export const kbdVariants = cva(
|
|
23
|
+
'bg-muted text-muted-foreground pointer-events-none inline-flex h-5 min-w-5 items-center justify-center gap-1 rounded-sm border border-border px-1 font-code text-code font-medium select-none',
|
|
24
|
+
{
|
|
25
|
+
variants: {
|
|
26
|
+
size: {
|
|
27
|
+
default: '',
|
|
28
|
+
compact: 'h-4 min-w-4 bg-muted/40 px-0.5 font-normal leading-none',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: {
|
|
32
|
+
size: 'default',
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
export type KbdSize = NonNullable<VariantProps<typeof kbdVariants>['size']>
|
|
38
|
+
|
|
39
|
+
export function Kbd(props: ComponentProps<'kbd'> & { size?: KbdSize }) {
|
|
40
|
+
const [local, rest] = splitProps(props, ['size', 'class'])
|
|
17
41
|
|
|
18
42
|
return (
|
|
19
|
-
<kbd
|
|
20
|
-
data-slot="kbd"
|
|
21
|
-
class={cn(
|
|
22
|
-
'bg-muted text-muted-foreground pointer-events-none inline-flex h-5 min-w-5 items-center justify-center gap-1 rounded-sm border border-border px-1 font-mono text-2xs font-medium select-none',
|
|
23
|
-
local.class
|
|
24
|
-
)}
|
|
25
|
-
{...rest}
|
|
26
|
-
/>
|
|
43
|
+
<kbd data-slot="kbd" class={cn(kbdVariants({ size: local.size }), local.class)} {...rest} />
|
|
27
44
|
)
|
|
28
45
|
}
|
|
29
46
|
|
|
30
|
-
export
|
|
31
|
-
|
|
47
|
+
export const kbdGroupVariants = cva('inline-flex items-center gap-1', {
|
|
48
|
+
variants: {
|
|
49
|
+
size: {
|
|
50
|
+
default: '',
|
|
51
|
+
/** The gap is what separates caps, so it tightens with them. */
|
|
52
|
+
compact: 'gap-0.5',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
defaultVariants: {
|
|
56
|
+
size: 'default',
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
export function KbdGroup(props: ComponentProps<'span'> & { size?: KbdSize }) {
|
|
61
|
+
const [local, rest] = splitProps(props, ['size', 'class'])
|
|
32
62
|
|
|
33
63
|
return (
|
|
34
64
|
<span
|
|
35
65
|
data-slot="kbd-group"
|
|
36
|
-
class={cn(
|
|
66
|
+
class={cn(kbdGroupVariants({ size: local.size }), local.class)}
|
|
37
67
|
{...rest}
|
|
38
68
|
/>
|
|
39
69
|
)
|
|
40
70
|
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Split a written chord into one entry per key cap. Every character is a key:
|
|
74
|
+
* `⌘K` renders as `⌘` + `K`, and a longer chord like `⇧⌘P` stays three caps
|
|
75
|
+
* wide, which matches how the system draws chords in menus.
|
|
76
|
+
*/
|
|
77
|
+
export function kbdChordKeys(keys: string): string[] {
|
|
78
|
+
return [...keys]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type KbdChordProps = Omit<ComponentProps<'span'>, 'children'> & {
|
|
82
|
+
/** The chord as it is drawn, e.g. `"⌘K"` or `"⇧⌘P"`. One cap per character. */
|
|
83
|
+
keys: string
|
|
84
|
+
size?: KbdSize
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* KbdChord.
|
|
89
|
+
*
|
|
90
|
+
* A written chord drawn as caps, one per character. Decoration only: the
|
|
91
|
+
* composition renders `aria-hidden`, so the accessible name and the parseable
|
|
92
|
+
* `aria-keyshortcuts` stay on the control the chord belongs to. That is why
|
|
93
|
+
* there is no children — the chord says what was drawn, nothing more.
|
|
94
|
+
*/
|
|
95
|
+
export function KbdChord(props: KbdChordProps) {
|
|
96
|
+
const [local, rest] = splitProps(props, ['keys', 'size', 'class'])
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<KbdGroup {...rest} size={local.size} class={local.class} aria-hidden="true">
|
|
100
|
+
<For each={kbdChordKeys(local.keys)}>{(key) => <Kbd size={local.size}>{key}</Kbd>}</For>
|
|
101
|
+
</KbdGroup>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
@@ -123,7 +123,7 @@ export function MenubarItem(
|
|
|
123
123
|
{local.shortcut ? (
|
|
124
124
|
<span
|
|
125
125
|
aria-hidden="true"
|
|
126
|
-
class="text-muted-foreground ms-auto font-
|
|
126
|
+
class="text-muted-foreground ms-auto font-code text-code tracking-widest"
|
|
127
127
|
>
|
|
128
128
|
{local.shortcut}
|
|
129
129
|
</span>
|
|
@@ -119,11 +119,9 @@ export function Heading<T extends ValidComponent = 'h2'>(props: HeadingProps<T>)
|
|
|
119
119
|
/**
|
|
120
120
|
* Text.
|
|
121
121
|
*
|
|
122
|
-
* Everything that is not a heading.
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* `micro` (11px, 500) is the floor — keyboard keys and the status bar. `code` is
|
|
126
|
-
* the mono face at 12px, for anything a user compares character by character.
|
|
122
|
+
* Everything that is not a heading. Body and strong copy use the content font
|
|
123
|
+
* axis; labels, captions and microcopy inherit the UI face. Code uses its own
|
|
124
|
+
* family and size axis for anything a user compares character by character.
|
|
127
125
|
*
|
|
128
126
|
* `tone` is a separate axis from `variant` because "secondary" is a colour
|
|
129
127
|
* decision, not a size: a muted caption and a muted body line are both common.
|
|
@@ -136,17 +134,17 @@ export const textVariants = cva('', {
|
|
|
136
134
|
variants: {
|
|
137
135
|
variant: {
|
|
138
136
|
/** 14px / 400. Body copy and descriptions. */
|
|
139
|
-
body: 'text-
|
|
137
|
+
body: 'font-content text-content',
|
|
140
138
|
/** 14px / 500. Labels, control text, list-row titles. */
|
|
141
139
|
label: 'text-sm font-medium',
|
|
142
140
|
/** 14px / 600. Emphasis inside body copy. */
|
|
143
|
-
strong: 'text-
|
|
141
|
+
strong: 'font-content text-content font-semibold',
|
|
144
142
|
/** 12px / 400. Metadata, secondary rows, units. */
|
|
145
143
|
caption: 'text-xs',
|
|
146
144
|
/** 11px / 500. The floor: keyboard keys, the status bar. */
|
|
147
145
|
micro: 'text-2xs font-medium',
|
|
148
146
|
/** 12px mono. Ids, paths, hashes, inline code. */
|
|
149
|
-
code: 'font-
|
|
147
|
+
code: 'font-code text-code',
|
|
150
148
|
},
|
|
151
149
|
tone: {
|
|
152
150
|
inherit: '',
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { fontOptions, type FontOption } from '#lib/font-catalog'
|
|
2
|
+
import { safeScriptStringLiteral } from '#lib/safe-script'
|
|
3
|
+
|
|
4
|
+
export const APPEARANCE_EDITOR_FONT_AXES = ['ui', 'content', 'code'] as const
|
|
5
|
+
|
|
6
|
+
export type AppearanceEditorFontAxis = (typeof APPEARANCE_EDITOR_FONT_AXES)[number]
|
|
7
|
+
export type AppearanceEditorFontFamilyId = FontOption['id']
|
|
8
|
+
|
|
9
|
+
export type AppearanceEditorFontSetting = Readonly<{
|
|
10
|
+
/** A stable id from the shared font catalogue, never a CSS family string. */
|
|
11
|
+
family: AppearanceEditorFontFamilyId
|
|
12
|
+
/** A pixel size, normalized to the supported range. */
|
|
13
|
+
size: number
|
|
14
|
+
}>
|
|
15
|
+
|
|
16
|
+
/** The host-owned choices edited by the shared appearance composition. */
|
|
17
|
+
export type AppearanceEditorFontSettings = Readonly<
|
|
18
|
+
Record<AppearanceEditorFontAxis, AppearanceEditorFontSetting>
|
|
19
|
+
>
|
|
20
|
+
|
|
21
|
+
export const APPEARANCE_EDITOR_FONT_SIZE_MIN = 10
|
|
22
|
+
export const APPEARANCE_EDITOR_FONT_SIZE_MAX = 32
|
|
23
|
+
|
|
24
|
+
/** New preferences use the platform face in each text role. */
|
|
25
|
+
export const DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS: AppearanceEditorFontSettings = Object.freeze({
|
|
26
|
+
ui: Object.freeze({ family: 'system', size: 14 }),
|
|
27
|
+
content: Object.freeze({ family: 'system', size: 14 }),
|
|
28
|
+
code: Object.freeze({ family: 'system', size: 12 }),
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
export type NormalizedAppearanceEditorFontSettings = Readonly<{
|
|
32
|
+
settings: AppearanceEditorFontSettings
|
|
33
|
+
/** Axes whose persisted family or size was unavailable or outside its limits. */
|
|
34
|
+
recoveredAxes: readonly AppearanceEditorFontAxis[]
|
|
35
|
+
}>
|
|
36
|
+
|
|
37
|
+
const fontFamilyIds: ReadonlySet<string> = new Set(fontOptions.map((option) => option.id))
|
|
38
|
+
const fontOptionsById = Object.freeze(
|
|
39
|
+
Object.fromEntries(fontOptions.map((option) => [option.id, option])) as Record<
|
|
40
|
+
AppearanceEditorFontFamilyId,
|
|
41
|
+
FontOption
|
|
42
|
+
>
|
|
43
|
+
)
|
|
44
|
+
const fontProjectionById = Object.freeze(
|
|
45
|
+
Object.fromEntries(
|
|
46
|
+
fontOptions.map((option) => [
|
|
47
|
+
option.id,
|
|
48
|
+
{
|
|
49
|
+
familyVariables: option.familyVariables,
|
|
50
|
+
uiTracking: option.uiTracking,
|
|
51
|
+
uiWordSpacing: option.uiWordSpacing,
|
|
52
|
+
},
|
|
53
|
+
])
|
|
54
|
+
) as Record<
|
|
55
|
+
AppearanceEditorFontFamilyId,
|
|
56
|
+
Pick<FontOption, 'familyVariables' | 'uiTracking' | 'uiWordSpacing'>
|
|
57
|
+
>
|
|
58
|
+
)
|
|
59
|
+
const fontAxisProjection = Object.freeze({
|
|
60
|
+
ui: Object.freeze({
|
|
61
|
+
familyAttribute: 'data-ui-font',
|
|
62
|
+
familyProperty: '--font-ui',
|
|
63
|
+
sizeProperty: '--font-ui-size',
|
|
64
|
+
scaleProperty: '--font-ui-scale',
|
|
65
|
+
defaultSize: DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS.ui.size,
|
|
66
|
+
}),
|
|
67
|
+
content: Object.freeze({
|
|
68
|
+
familyAttribute: 'data-content-font',
|
|
69
|
+
familyProperty: '--font-content',
|
|
70
|
+
sizeProperty: '--font-content-size',
|
|
71
|
+
scaleProperty: '--font-content-scale',
|
|
72
|
+
defaultSize: DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS.content.size,
|
|
73
|
+
}),
|
|
74
|
+
code: Object.freeze({
|
|
75
|
+
familyAttribute: 'data-code-font',
|
|
76
|
+
familyProperty: '--font-code',
|
|
77
|
+
sizeProperty: '--font-code-size',
|
|
78
|
+
scaleProperty: '--font-code-scale',
|
|
79
|
+
defaultSize: DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS.code.size,
|
|
80
|
+
}),
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
84
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function hasOwn(value: Record<string, unknown>, key: string): boolean {
|
|
88
|
+
return Object.prototype.hasOwnProperty.call(value, key)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function normalizeFamily(value: unknown): AppearanceEditorFontFamilyId | undefined {
|
|
92
|
+
return typeof value === 'string' && fontFamilyIds.has(value)
|
|
93
|
+
? (value as AppearanceEditorFontFamilyId)
|
|
94
|
+
: undefined
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function clampFontSize(value: number): number {
|
|
98
|
+
return Math.min(
|
|
99
|
+
APPEARANCE_EDITOR_FONT_SIZE_MAX,
|
|
100
|
+
Math.max(APPEARANCE_EDITOR_FONT_SIZE_MIN, Math.round(value))
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Normalize a single number-input value; invalid edits return the current size. */
|
|
105
|
+
export function normalizeAppearanceEditorFontSize(value: unknown, fallback: number): number {
|
|
106
|
+
const parsed =
|
|
107
|
+
typeof value === 'number'
|
|
108
|
+
? value
|
|
109
|
+
: typeof value === 'string' && value.trim() !== ''
|
|
110
|
+
? Number(value)
|
|
111
|
+
: Number.NaN
|
|
112
|
+
return Number.isFinite(parsed) ? clampFontSize(parsed) : clampFontSize(fallback)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Recover persisted font choices against the shared font catalogue.
|
|
117
|
+
*
|
|
118
|
+
* `legacyUiFamily` lets a host migrate its previous single font field into the
|
|
119
|
+
* UI axis. Content and code remain on the new System defaults during that
|
|
120
|
+
* migration. Missing settings are ordinary defaults; an unknown saved id or an
|
|
121
|
+
* invalid size is reported through `recoveredAxes` for the host's notice.
|
|
122
|
+
*/
|
|
123
|
+
export function normalizeAppearanceEditorFontSettings(
|
|
124
|
+
value: unknown,
|
|
125
|
+
legacyUiFamily?: unknown
|
|
126
|
+
): NormalizedAppearanceEditorFontSettings {
|
|
127
|
+
const source = isRecord(value) ? value : undefined
|
|
128
|
+
const malformedRoot = value !== undefined && value !== null && source === undefined
|
|
129
|
+
const recovered = new Set<AppearanceEditorFontAxis>()
|
|
130
|
+
const settings = {} as Record<AppearanceEditorFontAxis, AppearanceEditorFontSetting>
|
|
131
|
+
|
|
132
|
+
for (const axis of APPEARANCE_EDITOR_FONT_AXES) {
|
|
133
|
+
const axisPresent = source ? hasOwn(source, axis) : false
|
|
134
|
+
const axisValue = source?.[axis]
|
|
135
|
+
const axisSource = isRecord(axisValue) ? axisValue : undefined
|
|
136
|
+
const fallback = DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS[axis]
|
|
137
|
+
|
|
138
|
+
if (malformedRoot || (axisPresent && axisSource === undefined)) recovered.add(axis)
|
|
139
|
+
|
|
140
|
+
const hasFamily = axisSource ? hasOwn(axisSource, 'family') : false
|
|
141
|
+
const familyInput = hasFamily
|
|
142
|
+
? axisSource?.['family']
|
|
143
|
+
: axis === 'ui' && legacyUiFamily !== undefined
|
|
144
|
+
? legacyUiFamily
|
|
145
|
+
: undefined
|
|
146
|
+
const family = normalizeFamily(familyInput) ?? fallback.family
|
|
147
|
+
if (
|
|
148
|
+
(hasFamily || (axis === 'ui' && legacyUiFamily !== undefined)) &&
|
|
149
|
+
!normalizeFamily(familyInput)
|
|
150
|
+
) {
|
|
151
|
+
recovered.add(axis)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const hasSize = axisSource ? hasOwn(axisSource, 'size') : false
|
|
155
|
+
const sizeInput = axisSource?.['size']
|
|
156
|
+
const size = hasSize
|
|
157
|
+
? normalizeAppearanceEditorFontSize(sizeInput, fallback.size)
|
|
158
|
+
: fallback.size
|
|
159
|
+
if (
|
|
160
|
+
hasSize &&
|
|
161
|
+
(typeof sizeInput !== 'number' || !Number.isFinite(sizeInput) || sizeInput !== size)
|
|
162
|
+
) {
|
|
163
|
+
recovered.add(axis)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
settings[axis] = Object.freeze({ family, size })
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return Object.freeze({
|
|
170
|
+
settings: Object.freeze(settings),
|
|
171
|
+
recoveredAxes: Object.freeze(APPEARANCE_EDITOR_FONT_AXES.filter((axis) => recovered.has(axis))),
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Project normalized font preferences onto a document root.
|
|
177
|
+
*
|
|
178
|
+
* This is shared by `ThemeProvider` and hosts that own their own appearance
|
|
179
|
+
* persistence, so UI/content/code use the same catalogue ids, theme tokens,
|
|
180
|
+
* sizes, and projected CSS variables. The marker activates the optional role
|
|
181
|
+
* stylesheet and survives CSS import reordering.
|
|
182
|
+
*/
|
|
183
|
+
export function applyAppearanceFontSettings(
|
|
184
|
+
root: HTMLElement,
|
|
185
|
+
value: unknown
|
|
186
|
+
): NormalizedAppearanceEditorFontSettings {
|
|
187
|
+
const normalized = normalizeAppearanceEditorFontSettings(value)
|
|
188
|
+
const { settings } = normalized
|
|
189
|
+
|
|
190
|
+
root.removeAttribute('data-font')
|
|
191
|
+
root.setAttribute('data-font-settings', '')
|
|
192
|
+
for (const axis of APPEARANCE_EDITOR_FONT_AXES) {
|
|
193
|
+
const projection = fontAxisProjection[axis]
|
|
194
|
+
const setting = settings[axis]
|
|
195
|
+
const option = fontOptionsById[setting.family]
|
|
196
|
+
if (setting.family === 'system') root.removeAttribute(projection.familyAttribute)
|
|
197
|
+
else root.setAttribute(projection.familyAttribute, setting.family)
|
|
198
|
+
root.style.setProperty(projection.familyProperty, `var(${option.familyVariables[axis]})`)
|
|
199
|
+
root.style.setProperty(projection.sizeProperty, String(setting.size) + 'px')
|
|
200
|
+
root.style.setProperty(projection.scaleProperty, String(setting.size / projection.defaultSize))
|
|
201
|
+
if (axis === 'ui') {
|
|
202
|
+
root.style.setProperty('--ui-tracking', option.uiTracking)
|
|
203
|
+
root.style.setProperty('--ui-word-spacing', option.uiWordSpacing)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return normalized
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Generate a safe inline prepaint script for a host-owned appearance record.
|
|
212
|
+
*
|
|
213
|
+
* The stored object is read from `localStorage[storageKey].fonts`. A legacy
|
|
214
|
+
* top-level `font` family migrates into UI only; content/code use the shared
|
|
215
|
+
* defaults. The script applies the same catalogue whitelist, limits, attributes,
|
|
216
|
+
* and CSS properties as `applyAppearanceFontSettings` before styles paint. When
|
|
217
|
+
* `persistedVersion` is supplied, only a record with that exact top-level version
|
|
218
|
+
* is used; absent or mismatched records receive the System defaults.
|
|
219
|
+
*/
|
|
220
|
+
export function fontSettingsBootstrapScript(storageKey: string, persistedVersion?: number): string {
|
|
221
|
+
if (persistedVersion !== undefined && !Number.isSafeInteger(persistedVersion)) {
|
|
222
|
+
throw new RangeError('persistedVersion must be a safe integer')
|
|
223
|
+
}
|
|
224
|
+
const axisConfig = JSON.stringify(fontAxisProjection)
|
|
225
|
+
const fontProjection = JSON.stringify(fontProjectionById)
|
|
226
|
+
const fontIds = `[${fontOptions.map((option) => safeScriptStringLiteral(option.id)).join(',')}]`
|
|
227
|
+
const persistedVersionGuard =
|
|
228
|
+
persistedVersion === undefined
|
|
229
|
+
? ''
|
|
230
|
+
: `&&parsed.version===Number(${safeScriptStringLiteral(String(persistedVersion))})`
|
|
231
|
+
|
|
232
|
+
return `(function(){var r=document.documentElement;var axes=JSON.parse(${safeScriptStringLiteral(axisConfig)});var projection=JSON.parse(${safeScriptStringLiteral(fontProjection)});var ids=${fontIds};var has=function(v,k){return Object.prototype.hasOwnProperty.call(v,k)};var record=function(v){return typeof v==='object'&&v!==null&&!Array.isArray(v)};var p={};try{var raw=localStorage.getItem(${safeScriptStringLiteral(storageKey)});var parsed=raw?JSON.parse(raw):{};if(record(parsed)${persistedVersionGuard})p=parsed;}catch(e){}var saved=record(p.fonts)?p.fonts:{};var valid=function(v,d){return ids.indexOf(v)!==-1?v:d};var size=function(v,d){var n=typeof v==='number'?v:typeof v==='string'&&v.trim()!==''?Number(v):NaN;return Number.isFinite(n)?Math.min(${APPEARANCE_EDITOR_FONT_SIZE_MAX},Math.max(${APPEARANCE_EDITOR_FONT_SIZE_MIN},Math.round(n))):d};r.setAttribute('data-font-settings','');for(var axis in axes){if(!has(axes,axis))continue;var spec=axes[axis];var candidate=saved[axis];var value=record(candidate)?candidate:{};var legacyUi=axis==='ui'&&!has(value,'family');var family=legacyUi?valid(p.font,'system'):valid(value.family,'system');var font=projection[family];if(family==='system')r.removeAttribute(spec.familyAttribute);else r.setAttribute(spec.familyAttribute,family);r.style.setProperty(spec.familyProperty,'var('+font.familyVariables[axis]+')');var px=size(value.size,spec.defaultSize);r.style.setProperty(spec.sizeProperty,px+'px');r.style.setProperty(spec.scaleProperty,String(px/spec.defaultSize));if(axis==='ui'){r.style.setProperty('--ui-tracking',font.uiTracking);r.style.setProperty('--ui-word-spacing',font.uiWordSpacing);}}r.removeAttribute('data-font');})();`
|
|
233
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable font-family choices shared by the appearance editor and its host APIs.
|
|
3
|
+
*
|
|
4
|
+
* This stays independent of the theme-token manifest so consumers that need
|
|
5
|
+
* only font settings do not retain the palette catalogue.
|
|
6
|
+
*/
|
|
7
|
+
// Explicit catalogue choices resolve to the same named face in every role.
|
|
8
|
+
// System Code is the exception: it resolves to the platform monospace stack.
|
|
9
|
+
const FONT_OPTIONS = [
|
|
10
|
+
{
|
|
11
|
+
id: 'space-grotesk',
|
|
12
|
+
label: 'Space Grotesk',
|
|
13
|
+
description: 'The self-hosted Space Grotesk family.',
|
|
14
|
+
familyVariables: {
|
|
15
|
+
ui: '--font-family-space-grotesk',
|
|
16
|
+
content: '--font-family-space-grotesk',
|
|
17
|
+
code: '--font-family-space-grotesk',
|
|
18
|
+
},
|
|
19
|
+
uiTracking: 'normal',
|
|
20
|
+
uiWordSpacing: 'normal',
|
|
21
|
+
stack: 'both',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'system',
|
|
25
|
+
label: 'System',
|
|
26
|
+
description: "The platform's own interface family and system monospace counterpart.",
|
|
27
|
+
familyVariables: {
|
|
28
|
+
ui: '--font-family-system',
|
|
29
|
+
content: '--font-family-system',
|
|
30
|
+
code: '--font-family-system-mono',
|
|
31
|
+
},
|
|
32
|
+
uiTracking: 'normal',
|
|
33
|
+
uiWordSpacing: 'normal',
|
|
34
|
+
stack: 'both',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'geist',
|
|
38
|
+
label: 'Geist',
|
|
39
|
+
description: 'The face cortana already ships. Neutral and wide.',
|
|
40
|
+
familyVariables: {
|
|
41
|
+
ui: '--font-family-geist',
|
|
42
|
+
content: '--font-family-geist',
|
|
43
|
+
code: '--font-family-geist',
|
|
44
|
+
},
|
|
45
|
+
uiTracking: 'normal',
|
|
46
|
+
uiWordSpacing: 'normal',
|
|
47
|
+
stack: 'both',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'geist-mono',
|
|
51
|
+
label: 'Geist Mono',
|
|
52
|
+
description: 'Monospace throughout, for someone who wants a uniform texture.',
|
|
53
|
+
familyVariables: {
|
|
54
|
+
ui: '--font-family-geist-mono',
|
|
55
|
+
content: '--font-family-geist-mono',
|
|
56
|
+
code: '--font-family-geist-mono',
|
|
57
|
+
},
|
|
58
|
+
uiTracking: '-0.03em',
|
|
59
|
+
uiWordSpacing: '-1.5px',
|
|
60
|
+
stack: 'both',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'jetbrains-mono',
|
|
64
|
+
label: 'JetBrains Mono',
|
|
65
|
+
description: 'Monospace throughout, with coding ligatures off.',
|
|
66
|
+
familyVariables: {
|
|
67
|
+
ui: '--font-family-jetbrains-mono',
|
|
68
|
+
content: '--font-family-jetbrains-mono',
|
|
69
|
+
code: '--font-family-jetbrains-mono',
|
|
70
|
+
},
|
|
71
|
+
uiTracking: '-0.04em',
|
|
72
|
+
uiWordSpacing: '-2.5px',
|
|
73
|
+
stack: 'both',
|
|
74
|
+
},
|
|
75
|
+
] as const
|
|
76
|
+
|
|
77
|
+
/** Stable catalogue record type; saved values never contain CSS family strings. */
|
|
78
|
+
export type FontOption = (typeof FONT_OPTIONS)[number]
|
|
79
|
+
|
|
80
|
+
export const fontOptions: readonly FontOption[] = Object.freeze(FONT_OPTIONS)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Escape a string literal for an inline script's JavaScript source. */
|
|
2
|
+
export function safeScriptStringLiteral(value: string): string {
|
|
3
|
+
return JSON.stringify(value).replace(
|
|
4
|
+
/[<>\u2028\u2029]/g,
|
|
5
|
+
(character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, '0')}`
|
|
6
|
+
)
|
|
7
|
+
}
|
package/src/lib/tokens.ts
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { ACCENTS } from '@adea-ai/themes'
|
|
21
|
+
export { fontOptions } from './font-catalog'
|
|
22
|
+
export type { FontOption } from './font-catalog'
|
|
21
23
|
|
|
22
24
|
export type TokenKind =
|
|
23
25
|
| 'color'
|
|
@@ -388,20 +390,35 @@ export const radiusTokens: TokenDefinition[] = [
|
|
|
388
390
|
]
|
|
389
391
|
|
|
390
392
|
export const typographyTokens: TokenDefinition[] = [
|
|
393
|
+
{
|
|
394
|
+
name: 'font-ui',
|
|
395
|
+
kind: 'font-family',
|
|
396
|
+
description: 'The selected family for interface controls, labels and headings.',
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
name: 'font-content',
|
|
400
|
+
kind: 'font-family',
|
|
401
|
+
description: 'The selected family for prose, transcripts and long-form reading.',
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
name: 'font-code',
|
|
405
|
+
kind: 'font-family',
|
|
406
|
+
description: 'The selected family for code, terminals, keyboard keys, ids and paths.',
|
|
407
|
+
},
|
|
391
408
|
{
|
|
392
409
|
name: 'font-sans',
|
|
393
410
|
kind: 'font-family',
|
|
394
|
-
description: '
|
|
411
|
+
description: 'Compatibility alias for the selected UI family.',
|
|
395
412
|
},
|
|
396
413
|
{
|
|
397
414
|
name: 'font-mono',
|
|
398
415
|
kind: 'font-family',
|
|
399
|
-
description: '
|
|
416
|
+
description: 'Compatibility alias for the selected code family.',
|
|
400
417
|
},
|
|
401
418
|
{
|
|
402
419
|
name: 'font-family-space-grotesk',
|
|
403
420
|
kind: 'font-family',
|
|
404
|
-
description: 'The
|
|
421
|
+
description: 'The self-hosted Space Grotesk family.',
|
|
405
422
|
},
|
|
406
423
|
{
|
|
407
424
|
name: 'font-family-jetbrains-mono',
|
|
@@ -428,6 +445,36 @@ export const typographyTokens: TokenDefinition[] = [
|
|
|
428
445
|
kind: 'font-family',
|
|
429
446
|
description: "The platform's own monospace face.",
|
|
430
447
|
},
|
|
448
|
+
{
|
|
449
|
+
name: 'font-ui-size',
|
|
450
|
+
kind: 'dimension',
|
|
451
|
+
description: 'Runtime-projected interface size in pixels; scales the shared UI text ladder.',
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
name: 'font-content-size',
|
|
455
|
+
kind: 'dimension',
|
|
456
|
+
description: 'Runtime-projected prose and transcript size in pixels.',
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
name: 'font-code-size',
|
|
460
|
+
kind: 'dimension',
|
|
461
|
+
description: 'Runtime-projected code, terminal and keyboard-key size in pixels.',
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
name: 'font-ui-scale',
|
|
465
|
+
kind: 'number',
|
|
466
|
+
description: 'The UI size ratio applied to the interface text ladder.',
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
name: 'font-content-scale',
|
|
470
|
+
kind: 'number',
|
|
471
|
+
description: 'The content size ratio applied to rem-relative prose text.',
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
name: 'font-code-scale',
|
|
475
|
+
kind: 'number',
|
|
476
|
+
description: 'The code size ratio applied to rem-relative code text.',
|
|
477
|
+
},
|
|
431
478
|
{
|
|
432
479
|
name: 'ui-tracking',
|
|
433
480
|
kind: 'text',
|
|
@@ -443,13 +490,13 @@ export const typographyTokens: TokenDefinition[] = [
|
|
|
443
490
|
{
|
|
444
491
|
name: 'text-2xs',
|
|
445
492
|
kind: 'text',
|
|
446
|
-
description: '
|
|
493
|
+
description: 'The smallest UI-scaled label: status bar and compact metadata.',
|
|
447
494
|
},
|
|
448
|
-
{ name: 'text-xs', kind: 'text', description: '
|
|
495
|
+
{ name: 'text-xs', kind: 'text', description: 'UI-scaled metadata, badges and secondary rows.' },
|
|
449
496
|
{
|
|
450
497
|
name: 'text-sm',
|
|
451
498
|
kind: 'text',
|
|
452
|
-
description: '
|
|
499
|
+
description: 'The UI-scaled interface default for labels and controls.',
|
|
453
500
|
},
|
|
454
501
|
{ name: 'text-base', kind: 'text', description: '16px — card titles and dialog titles.' },
|
|
455
502
|
{ name: 'text-lg', kind: 'text', description: '18px — a section heading inside a page.' },
|
|
@@ -458,7 +505,17 @@ export const typographyTokens: TokenDefinition[] = [
|
|
|
458
505
|
{
|
|
459
506
|
name: 'text-3xl',
|
|
460
507
|
kind: 'text',
|
|
461
|
-
description: '
|
|
508
|
+
description: 'The largest UI-scaled rung. Reserved for an empty-state headline.',
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
name: 'text-content',
|
|
512
|
+
kind: 'text',
|
|
513
|
+
description: 'The selected content size with the prose line-height.',
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
name: 'text-code',
|
|
517
|
+
kind: 'text',
|
|
518
|
+
description: 'The selected code size with the code line-height.',
|
|
462
519
|
},
|
|
463
520
|
]
|
|
464
521
|
|
|
@@ -661,58 +718,6 @@ export const accentPresets: readonly AccentPreset[] = Object.freeze([
|
|
|
661
718
|
...ACCENTS,
|
|
662
719
|
])
|
|
663
720
|
|
|
664
|
-
/**
|
|
665
|
-
* The font axis.
|
|
666
|
-
*
|
|
667
|
-
* The family is a selection, like the accent: `data-font` sits on the same element
|
|
668
|
-
* and each option swaps the interface face. `space-grotesk` is the default because
|
|
669
|
-
* it is the face the visual language was drawn against, and `system` is a
|
|
670
|
-
* first-class option rather than a fallback — an application that wants the
|
|
671
|
-
* platform's own face should be able to say so without losing the rest of the
|
|
672
|
-
* system.
|
|
673
|
-
*/
|
|
674
|
-
export type FontOption = {
|
|
675
|
-
/** The `data-font` value. `space-grotesk` is the default and needs no attribute. */
|
|
676
|
-
id: string
|
|
677
|
-
label: string
|
|
678
|
-
description: string
|
|
679
|
-
/** Which of the two stacks this option sets. */
|
|
680
|
-
stack: 'sans' | 'mono' | 'both'
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
export const fontOptions: readonly FontOption[] = Object.freeze([
|
|
684
|
-
{
|
|
685
|
-
id: 'space-grotesk',
|
|
686
|
-
label: 'Space Grotesk',
|
|
687
|
-
description: 'The default, and the face the visual language was drawn against.',
|
|
688
|
-
stack: 'both',
|
|
689
|
-
},
|
|
690
|
-
{
|
|
691
|
-
id: 'system',
|
|
692
|
-
label: 'System',
|
|
693
|
-
description: "The platform's own face. What a terminal or editor usually wants.",
|
|
694
|
-
stack: 'both',
|
|
695
|
-
},
|
|
696
|
-
{
|
|
697
|
-
id: 'geist',
|
|
698
|
-
label: 'Geist',
|
|
699
|
-
description: 'The face cortana already ships. Neutral and wide.',
|
|
700
|
-
stack: 'both',
|
|
701
|
-
},
|
|
702
|
-
{
|
|
703
|
-
id: 'geist-mono',
|
|
704
|
-
label: 'Geist Mono',
|
|
705
|
-
description: 'Monospace throughout, for someone who wants a uniform texture.',
|
|
706
|
-
stack: 'both',
|
|
707
|
-
},
|
|
708
|
-
{
|
|
709
|
-
id: 'jetbrains-mono',
|
|
710
|
-
label: 'JetBrains Mono',
|
|
711
|
-
description: 'Monospace throughout, with coding ligatures off.',
|
|
712
|
-
stack: 'both',
|
|
713
|
-
},
|
|
714
|
-
])
|
|
715
|
-
|
|
716
721
|
/** Every token, in gallery order. */
|
|
717
722
|
export const designTokens = {
|
|
718
723
|
color: colorTokens,
|