@delacour/react-native-ui 0.1.0-alpha.3
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 +113 -0
- package/package.json +125 -0
- package/src/components/accordion/AGENTS.md +192 -0
- package/src/components/accordion/accordion-content.tsx +125 -0
- package/src/components/accordion/accordion-description.tsx +17 -0
- package/src/components/accordion/accordion-indicator.tsx +104 -0
- package/src/components/accordion/accordion-item.tsx +78 -0
- package/src/components/accordion/accordion-title.tsx +18 -0
- package/src/components/accordion/accordion-trigger.tsx +148 -0
- package/src/components/accordion/accordion.context.tsx +153 -0
- package/src/components/accordion/accordion.tsx +253 -0
- package/src/components/accordion/accordion.types.ts +11 -0
- package/src/components/accordion/accordion.variants.test.ts +435 -0
- package/src/components/accordion/accordion.variants.ts +358 -0
- package/src/components/accordion/index.ts +45 -0
- package/src/components/badge/AGENTS.md +83 -0
- package/src/components/badge/badge-close-button.tsx +48 -0
- package/src/components/badge/badge-end-content.tsx +15 -0
- package/src/components/badge/badge-label.tsx +24 -0
- package/src/components/badge/badge-start-content.tsx +16 -0
- package/src/components/badge/badge.context.tsx +64 -0
- package/src/components/badge/badge.tsx +192 -0
- package/src/components/badge/badge.types.ts +10 -0
- package/src/components/badge/badge.variants.test.ts +269 -0
- package/src/components/badge/badge.variants.ts +238 -0
- package/src/components/badge/index.ts +17 -0
- package/src/components/bottom-sheet/AGENTS.md +232 -0
- package/src/components/bottom-sheet/bottom-sheet-background.tsx +35 -0
- package/src/components/bottom-sheet/bottom-sheet-close.tsx +60 -0
- package/src/components/bottom-sheet/bottom-sheet-container.tsx +245 -0
- package/src/components/bottom-sheet/bottom-sheet-content.tsx +66 -0
- package/src/components/bottom-sheet/bottom-sheet-description.tsx +29 -0
- package/src/components/bottom-sheet/bottom-sheet-footer.tsx +148 -0
- package/src/components/bottom-sheet/bottom-sheet-handle.tsx +47 -0
- package/src/components/bottom-sheet/bottom-sheet-overlay.tsx +180 -0
- package/src/components/bottom-sheet/bottom-sheet-portal.tsx +72 -0
- package/src/components/bottom-sheet/bottom-sheet-scroll-view.tsx +95 -0
- package/src/components/bottom-sheet/bottom-sheet-title.tsx +24 -0
- package/src/components/bottom-sheet/bottom-sheet-trigger.tsx +73 -0
- package/src/components/bottom-sheet/bottom-sheet.context.tsx +135 -0
- package/src/components/bottom-sheet/bottom-sheet.tsx +125 -0
- package/src/components/bottom-sheet/bottom-sheet.variants.test.ts +276 -0
- package/src/components/bottom-sheet/bottom-sheet.variants.ts +199 -0
- package/src/components/bottom-sheet/index.ts +40 -0
- package/src/components/bottom-sheet/use-bottom-sheet-input.ts +125 -0
- package/src/components/button/AGENTS.md +217 -0
- package/src/components/button/button-end-content.tsx +15 -0
- package/src/components/button/button-group-separator.tsx +37 -0
- package/src/components/button/button-group-text.tsx +101 -0
- package/src/components/button/button-group.tsx +158 -0
- package/src/components/button/button-label.tsx +24 -0
- package/src/components/button/button-start-content.tsx +15 -0
- package/src/components/button/button.context.tsx +187 -0
- package/src/components/button/button.tsx +273 -0
- package/src/components/button/button.types.ts +10 -0
- package/src/components/button/button.variants.test.ts +811 -0
- package/src/components/button/button.variants.ts +480 -0
- package/src/components/button/index.ts +50 -0
- package/src/components/chart/AGENTS.md +210 -0
- package/src/components/chart/chart-area.tsx +93 -0
- package/src/components/chart/chart-bar.tsx +103 -0
- package/src/components/chart/chart-bars.tsx +75 -0
- package/src/components/chart/chart-candlestick.tsx +60 -0
- package/src/components/chart/chart-grid.tsx +33 -0
- package/src/components/chart/chart-legend.tsx +41 -0
- package/src/components/chart/chart-line.tsx +50 -0
- package/src/components/chart/chart-scatter.tsx +35 -0
- package/src/components/chart/chart-tooltip-dot.tsx +70 -0
- package/src/components/chart/chart-tooltip-x.tsx +121 -0
- package/src/components/chart/chart-tooltip-y.tsx +55 -0
- package/src/components/chart/chart-tooltip.tsx +133 -0
- package/src/components/chart/chart-x-axis.tsx +27 -0
- package/src/components/chart/chart-y-axis.tsx +21 -0
- package/src/components/chart/chart.context.tsx +127 -0
- package/src/components/chart/chart.tsx +448 -0
- package/src/components/chart/chart.types.ts +108 -0
- package/src/components/chart/chart.variants.test.ts +893 -0
- package/src/components/chart/chart.variants.ts +726 -0
- package/src/components/chart/index.ts +103 -0
- package/src/components/chart/pie-chart-center.tsx +40 -0
- package/src/components/chart/pie-chart-label.tsx +56 -0
- package/src/components/chart/pie-chart-slice.tsx +45 -0
- package/src/components/chart/pie-chart-tooltip.tsx +85 -0
- package/src/components/chart/pie-chart.context.tsx +77 -0
- package/src/components/chart/pie-chart.tsx +247 -0
- package/src/components/chart/use-chart-font.ts +25 -0
- package/src/components/chart/use-chart-palette.ts +38 -0
- package/src/components/checkbox/AGENTS.md +185 -0
- package/src/components/checkbox/checkbox-box.tsx +152 -0
- package/src/components/checkbox/checkbox-group.tsx +107 -0
- package/src/components/checkbox/checkbox-label.tsx +34 -0
- package/src/components/checkbox/checkbox.context.tsx +136 -0
- package/src/components/checkbox/checkbox.tsx +258 -0
- package/src/components/checkbox/checkbox.types.ts +14 -0
- package/src/components/checkbox/checkbox.variants.test.ts +634 -0
- package/src/components/checkbox/checkbox.variants.ts +484 -0
- package/src/components/checkbox/index.ts +42 -0
- package/src/components/field/AGENTS.md +100 -0
- package/src/components/field/field-content.tsx +24 -0
- package/src/components/field/field-description.tsx +29 -0
- package/src/components/field/field-error.tsx +61 -0
- package/src/components/field/field-group.tsx +22 -0
- package/src/components/field/field-label.tsx +33 -0
- package/src/components/field/field-legend.tsx +34 -0
- package/src/components/field/field-separator.tsx +81 -0
- package/src/components/field/field-set.tsx +22 -0
- package/src/components/field/field.context.tsx +78 -0
- package/src/components/field/field.tsx +144 -0
- package/src/components/field/field.types.ts +15 -0
- package/src/components/field/field.variants.test.ts +216 -0
- package/src/components/field/field.variants.ts +136 -0
- package/src/components/field/index.ts +21 -0
- package/src/components/icon/AGENTS.md +104 -0
- package/src/components/icon/icon.context.tsx +29 -0
- package/src/components/icon/icon.tsx +92 -0
- package/src/components/icon/icon.variants.test.ts +111 -0
- package/src/components/icon/icon.variants.ts +79 -0
- package/src/components/icon/index.ts +11 -0
- package/src/components/input/AGENTS.md +126 -0
- package/src/components/input/index.ts +27 -0
- package/src/components/input/input-group-decorator.tsx +84 -0
- package/src/components/input/input-group-prefix.tsx +16 -0
- package/src/components/input/input-group-suffix.tsx +15 -0
- package/src/components/input/input-group.tsx +140 -0
- package/src/components/input/input.context.tsx +87 -0
- package/src/components/input/input.tsx +184 -0
- package/src/components/input/input.types.ts +10 -0
- package/src/components/input/input.variants.test.ts +369 -0
- package/src/components/input/input.variants.ts +329 -0
- package/src/components/list-group/AGENTS.md +67 -0
- package/src/components/list-group/index.ts +20 -0
- package/src/components/list-group/list-group-item-content.tsx +10 -0
- package/src/components/list-group/list-group-item-description.tsx +12 -0
- package/src/components/list-group/list-group-item-prefix.tsx +30 -0
- package/src/components/list-group/list-group-item-suffix.tsx +55 -0
- package/src/components/list-group/list-group-item-title.tsx +17 -0
- package/src/components/list-group/list-group-item.tsx +87 -0
- package/src/components/list-group/list-group.context.tsx +65 -0
- package/src/components/list-group/list-group.tsx +116 -0
- package/src/components/list-group/list-group.types.ts +14 -0
- package/src/components/list-group/list-group.variants.test.ts +223 -0
- package/src/components/list-group/list-group.variants.ts +107 -0
- package/src/components/pressable/AGENTS.md +64 -0
- package/src/components/pressable/index.ts +9 -0
- package/src/components/pressable/pressable.tsx +256 -0
- package/src/components/pressable/pressable.variants.test.ts +99 -0
- package/src/components/pressable/pressable.variants.ts +63 -0
- package/src/components/provider/AGENTS.md +90 -0
- package/src/components/provider/index.ts +1 -0
- package/src/components/provider/provider.tsx +105 -0
- package/src/components/radio/AGENTS.md +247 -0
- package/src/components/radio/index.ts +35 -0
- package/src/components/radio/radio-group.tsx +126 -0
- package/src/components/radio/radio-indicator.tsx +91 -0
- package/src/components/radio/radio-label.tsx +39 -0
- package/src/components/radio/radio.context.tsx +132 -0
- package/src/components/radio/radio.tsx +215 -0
- package/src/components/radio/radio.variants.test.ts +581 -0
- package/src/components/radio/radio.variants.ts +272 -0
- package/src/components/screen/AGENTS.md +287 -0
- package/src/components/screen/index.ts +85 -0
- package/src/components/screen/screen-chat-list.tsx +485 -0
- package/src/components/screen/screen-content.tsx +69 -0
- package/src/components/screen/screen-debug.ts +43 -0
- package/src/components/screen/screen-error.tsx +61 -0
- package/src/components/screen/screen-flat-list.tsx +88 -0
- package/src/components/screen/screen-footer-background.tsx +60 -0
- package/src/components/screen/screen-footer.tsx +170 -0
- package/src/components/screen/screen-header.tsx +36 -0
- package/src/components/screen/screen-legend-list.tsx +105 -0
- package/src/components/screen/screen-list-component.tsx +23 -0
- package/src/components/screen/screen-loading.tsx +50 -0
- package/src/components/screen/screen-navbar-back-button.tsx +79 -0
- package/src/components/screen/screen-navbar-background.tsx +45 -0
- package/src/components/screen/screen-navbar-subtitle.tsx +26 -0
- package/src/components/screen/screen-navbar-title.tsx +31 -0
- package/src/components/screen/screen-navbar.tsx +154 -0
- package/src/components/screen/screen-root.tsx +38 -0
- package/src/components/screen/screen-scroll-area.tsx +116 -0
- package/src/components/screen/screen-scroll-shadow.tsx +186 -0
- package/src/components/screen/screen-section-list.tsx +81 -0
- package/src/components/screen/screen-view.tsx +59 -0
- package/src/components/screen/screen.context.tsx +167 -0
- package/src/components/screen/screen.tsx +114 -0
- package/src/components/screen/screen.types.ts +66 -0
- package/src/components/screen/screen.variants.test.ts +602 -0
- package/src/components/screen/screen.variants.ts +475 -0
- package/src/components/screen/use-screen-scroll-insets.ts +218 -0
- package/src/components/separator/AGENTS.md +30 -0
- package/src/components/separator/index.ts +8 -0
- package/src/components/separator/separator.tsx +85 -0
- package/src/components/slider/AGENTS.md +274 -0
- package/src/components/slider/index.ts +48 -0
- package/src/components/slider/slider-fill.tsx +68 -0
- package/src/components/slider/slider-output.tsx +55 -0
- package/src/components/slider/slider-thumb.tsx +161 -0
- package/src/components/slider/slider-track.tsx +233 -0
- package/src/components/slider/slider.context.tsx +118 -0
- package/src/components/slider/slider.tsx +321 -0
- package/src/components/slider/slider.types.ts +26 -0
- package/src/components/slider/slider.variants.test.ts +804 -0
- package/src/components/slider/slider.variants.ts +607 -0
- package/src/components/spinner/AGENTS.md +73 -0
- package/src/components/spinner/index.ts +17 -0
- package/src/components/spinner/spinner-arc.tsx +90 -0
- package/src/components/spinner/spinner-content.tsx +63 -0
- package/src/components/spinner/spinner.context.tsx +52 -0
- package/src/components/spinner/spinner.tsx +128 -0
- package/src/components/spinner/spinner.variants.test.ts +273 -0
- package/src/components/spinner/spinner.variants.ts +187 -0
- package/src/components/switch/AGENTS.md +213 -0
- package/src/components/switch/index.ts +42 -0
- package/src/components/switch/switch-content.tsx +111 -0
- package/src/components/switch/switch-end-content.tsx +18 -0
- package/src/components/switch/switch-start-content.tsx +20 -0
- package/src/components/switch/switch-thumb.tsx +102 -0
- package/src/components/switch/switch.context.tsx +79 -0
- package/src/components/switch/switch.tsx +423 -0
- package/src/components/switch/switch.types.ts +14 -0
- package/src/components/switch/switch.variants.test.ts +570 -0
- package/src/components/switch/switch.variants.ts +511 -0
- package/src/components/tabs/AGENTS.md +286 -0
- package/src/components/tabs/index.ts +69 -0
- package/src/components/tabs/tabs-content.tsx +65 -0
- package/src/components/tabs/tabs-indicator.tsx +97 -0
- package/src/components/tabs/tabs-label.tsx +62 -0
- package/src/components/tabs/tabs-list.tsx +139 -0
- package/src/components/tabs/tabs-pager.tsx +59 -0
- package/src/components/tabs/tabs-scroll-view.tsx +171 -0
- package/src/components/tabs/tabs-separator.tsx +73 -0
- package/src/components/tabs/tabs-trigger.tsx +210 -0
- package/src/components/tabs/tabs.context.tsx +294 -0
- package/src/components/tabs/tabs.tsx +435 -0
- package/src/components/tabs/tabs.types.ts +13 -0
- package/src/components/tabs/tabs.variants.test.ts +1020 -0
- package/src/components/tabs/tabs.variants.ts +672 -0
- package/src/components/text/AGENTS.md +95 -0
- package/src/components/text/index.ts +25 -0
- package/src/components/text/text.context.tsx +60 -0
- package/src/components/text/text.tsx +251 -0
- package/src/components/text/text.variants.test.ts +422 -0
- package/src/components/text/text.variants.ts +282 -0
- package/src/display-name.test.ts +145 -0
- package/src/docs.test.ts +98 -0
- package/src/expo/navigation-theme.tsx +58 -0
- package/src/hooks/use-controllable-state.ts +45 -0
- package/src/hooks/use-keyboard-state-sync.tsx +147 -0
- package/src/hooks/use-navigation-theme.ts +78 -0
- package/src/hooks/use-theme-color.ts +44 -0
- package/src/icons/central.ts +1 -0
- package/src/lib/cn.test.ts +136 -0
- package/src/lib/cn.ts +29 -0
- package/src/lib/color.test.ts +80 -0
- package/src/lib/color.ts +79 -0
- package/src/lib/compose-refs.test.ts +64 -0
- package/src/lib/compose-refs.ts +37 -0
- package/src/lib/keyboard-animation.test.ts +33 -0
- package/src/lib/keyboard-animation.ts +28 -0
- package/src/lib/merge-props.test.ts +82 -0
- package/src/lib/merge-props.ts +47 -0
- package/src/lib/navigation-theme.test.ts +49 -0
- package/src/lib/navigation-theme.ts +53 -0
- package/src/lib/slot.tsx +45 -0
- package/src/lib/tv.ts +20 -0
- package/src/styles/base.css +2 -0
- package/src/styles/geometry.test.ts +117 -0
- package/src/styles/index.css +3 -0
- package/src/styles/theme-tokens.test.ts +252 -0
- package/src/styles/theme.css +447 -0
- package/src/styles/tokens.css +132 -0
- package/src/styles/tokens.test.ts +181 -0
- package/src/styles/tokens.ts +86 -0
- package/src/uniwind-env.d.ts +1 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
@theme {
|
|
2
|
+
/*
|
|
3
|
+
* The one number every generic corner is derived from, in the `@theme
|
|
4
|
+
* inline` block at the bottom of this file. shadcn's own scale, so a
|
|
5
|
+
* `--radius` copied out of a web app retunes every rounded edge here at
|
|
6
|
+
* once — and unlike the older `calc(var(--radius) - 4px)` form some theme
|
|
7
|
+
* generators still emit, a multiplier scales rather than shifts, so a
|
|
8
|
+
* square-cornered theme stays square at every step.
|
|
9
|
+
*
|
|
10
|
+
* It lives in `tokens.css` rather than beside the palette because
|
|
11
|
+
* `apps/web` imports only this file; a `--radius` in `theme.css` would
|
|
12
|
+
* leave every derived radius undefined on the docs site.
|
|
13
|
+
*/
|
|
14
|
+
--radius: 0.625rem;
|
|
15
|
+
|
|
16
|
+
--text-xs: 12px;
|
|
17
|
+
--text-sm: 14px;
|
|
18
|
+
--text-base: 16px;
|
|
19
|
+
--text-lg: 18px;
|
|
20
|
+
--text-xl: 20px;
|
|
21
|
+
--text-2xl: 24px;
|
|
22
|
+
--text-3xl: 30px;
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* The button's three scales, stepped together: the height — which is also
|
|
26
|
+
* the square footprint at an `icon-*` size — the label's type, and the
|
|
27
|
+
* corner.
|
|
28
|
+
*
|
|
29
|
+
* The corner is half the height at every step, so a button is a capsule and
|
|
30
|
+
* a square one a circle. A token rather than `rounded-full` because the
|
|
31
|
+
* shape is a default, not a law: a kit that wants squarer buttons changes
|
|
32
|
+
* three numbers here and touches no component. `tokens.test.ts` keeps each
|
|
33
|
+
* under half its height — past that the renderer clamps and the number
|
|
34
|
+
* stops meaning what it says.
|
|
35
|
+
*/
|
|
36
|
+
--spacing-button-sm: 36px;
|
|
37
|
+
--spacing-button-md: 44px;
|
|
38
|
+
--spacing-button-lg: 52px;
|
|
39
|
+
|
|
40
|
+
--text-button-sm: 14px;
|
|
41
|
+
--text-button-md: 16px;
|
|
42
|
+
--text-button-lg: 18px;
|
|
43
|
+
|
|
44
|
+
--radius-button-sm: 18px;
|
|
45
|
+
--radius-button-md: 22px;
|
|
46
|
+
--radius-button-lg: 26px;
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* Field heights, and the type scale paired step-for-step with them.
|
|
50
|
+
*
|
|
51
|
+
* A scale of its own rather than `--spacing-button-*`, even though the
|
|
52
|
+
* numbers agree today: a control that borrowed the button's would retune
|
|
53
|
+
* silently whenever the button's did.
|
|
54
|
+
*/
|
|
55
|
+
--spacing-input-sm: 36px;
|
|
56
|
+
--spacing-input-md: 44px;
|
|
57
|
+
--spacing-input-lg: 52px;
|
|
58
|
+
|
|
59
|
+
--text-input-sm: 14px;
|
|
60
|
+
--text-input-md: 16px;
|
|
61
|
+
--text-input-lg: 18px;
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
* The icon scale, shared by `Icon` and `Spinner` so a named size means the
|
|
65
|
+
* same edge length in both and one can replace the other without the
|
|
66
|
+
* layout moving. `size-icon-md`.
|
|
67
|
+
*
|
|
68
|
+
* A component's own size axis indexes into this scale rather than restating
|
|
69
|
+
* a number: a button's sm/md/lg icon is icon-sm/md/lg.
|
|
70
|
+
*
|
|
71
|
+
* It sizes more than glyphs. A `Checkbox`'s square reads it too, two steps
|
|
72
|
+
* above the tick it holds, rather than minting a scale that would have to be
|
|
73
|
+
* retuned in step with this one forever.
|
|
74
|
+
*/
|
|
75
|
+
--spacing-icon-xs: 14px;
|
|
76
|
+
--spacing-icon-sm: 16px;
|
|
77
|
+
--spacing-icon-md: 18px;
|
|
78
|
+
--spacing-icon-lg: 20px;
|
|
79
|
+
--spacing-icon-xl: 24px;
|
|
80
|
+
--spacing-icon-2xl: 32px;
|
|
81
|
+
|
|
82
|
+
/*
|
|
83
|
+
* Screen chrome. `h-navbar-row` is the navbar's control row on its own —
|
|
84
|
+
* the safe-area band above it is padding, not part of this number, so one
|
|
85
|
+
* value holds across every device.
|
|
86
|
+
*
|
|
87
|
+
* `px-screen-gutter` is the horizontal gutter `Screen.Header`,
|
|
88
|
+
* `Screen.Navbar` and a screen's own content all line up against. It is one
|
|
89
|
+
* token rather than three `px-5`s so a change moves every edge together.
|
|
90
|
+
*
|
|
91
|
+
* The footer's padding is deliberately NOT here — see `screen.variants.ts`.
|
|
92
|
+
*/
|
|
93
|
+
--spacing-navbar-row: 56px;
|
|
94
|
+
--spacing-screen-gutter: 20px;
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
* A chart's height. It has no intrinsic one — a canvas fills whatever box
|
|
98
|
+
* it is given — so without a token every chart on a dashboard would be a
|
|
99
|
+
* different height and the rows would not line up.
|
|
100
|
+
*
|
|
101
|
+
* A height rather than an aspect ratio on purpose: two charts side by side
|
|
102
|
+
* at different widths must still agree, and `aspect-video` would give them
|
|
103
|
+
* different heights. Registering the names with tailwind-merge is what lets
|
|
104
|
+
* a caller's `aspect-video h-auto` cleanly win when they want that instead.
|
|
105
|
+
*/
|
|
106
|
+
--spacing-chart-sm: 160px;
|
|
107
|
+
--spacing-chart-md: 220px;
|
|
108
|
+
--spacing-chart-lg: 280px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/*
|
|
112
|
+
* The generic corner scale, derived from `--radius`.
|
|
113
|
+
*
|
|
114
|
+
* `inline` for the same reason the palette uses it: a utility has to read
|
|
115
|
+
* `var(--radius)` itself, so a theme that redeclares `--radius` reaches
|
|
116
|
+
* `rounded-lg` rather than a copy taken when the scale was compiled.
|
|
117
|
+
*
|
|
118
|
+
* `--radius-button-*` above is deliberately NOT part of this. A button's corner
|
|
119
|
+
* is half its own height — a shape, not a step on a scale — and it must not
|
|
120
|
+
* move when a consumer retunes `--radius`.
|
|
121
|
+
*/
|
|
122
|
+
@theme inline {
|
|
123
|
+
--radius-xs: calc(var(--radius) * 0.4);
|
|
124
|
+
--radius-sm: calc(var(--radius) * 0.6);
|
|
125
|
+
--radius-md: calc(var(--radius) * 0.8);
|
|
126
|
+
--radius-lg: var(--radius);
|
|
127
|
+
--radius-xl: calc(var(--radius) * 1.4);
|
|
128
|
+
--radius-2xl: calc(var(--radius) * 1.8);
|
|
129
|
+
--radius-3xl: calc(var(--radius) * 2.2);
|
|
130
|
+
--radius-4xl: calc(var(--radius) * 2.6);
|
|
131
|
+
--radius-full: 9999px;
|
|
132
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
BUTTON_RADIUS_TOKENS,
|
|
6
|
+
BUTTON_SIZE_TOKENS,
|
|
7
|
+
BUTTON_TEXT_TOKENS,
|
|
8
|
+
CHART_SIZE_TOKENS,
|
|
9
|
+
ICON_SIZE_TOKENS,
|
|
10
|
+
INPUT_SIZE_TOKENS,
|
|
11
|
+
INPUT_TEXT_TOKENS,
|
|
12
|
+
SCREEN_SIZE_TOKENS,
|
|
13
|
+
} from "./tokens";
|
|
14
|
+
|
|
15
|
+
const CSS = readFileSync(join(import.meta.dirname, "tokens.css"), "utf-8");
|
|
16
|
+
|
|
17
|
+
/** Every `--name: 12px` declaration in `tokens.css`, as a name → points map. */
|
|
18
|
+
function declarations(): Map<string, number> {
|
|
19
|
+
const found = new Map<string, number>();
|
|
20
|
+
for (const [, name, value] of CSS.matchAll(/--([\w-]+):\s*([\d.]+)px;/g)) {
|
|
21
|
+
found.set(name, Number(value));
|
|
22
|
+
}
|
|
23
|
+
return found;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const TOKENS = declarations();
|
|
27
|
+
|
|
28
|
+
/** Points behind a token, failing loudly rather than yielding NaN. */
|
|
29
|
+
function px(name: string): number {
|
|
30
|
+
const value = TOKENS.get(name);
|
|
31
|
+
if (value === undefined) throw new Error(`tokens.css defines no --${name}`);
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe("tokens.ts and tokens.css", () => {
|
|
36
|
+
// The registry drives `cn`'s tailwind-merge extension. A token in the CSS
|
|
37
|
+
// that is missing here is an override that silently stops working; a name
|
|
38
|
+
// here with no CSS behind it is a class that compiles to nothing.
|
|
39
|
+
test("declare exactly the same spacing tokens", () => {
|
|
40
|
+
const declared = [...TOKENS.keys()]
|
|
41
|
+
.filter((name) => name.startsWith("spacing-"))
|
|
42
|
+
.map((name) => name.replace("spacing-", ""))
|
|
43
|
+
.sort();
|
|
44
|
+
const registered = [
|
|
45
|
+
...BUTTON_SIZE_TOKENS,
|
|
46
|
+
...CHART_SIZE_TOKENS,
|
|
47
|
+
...ICON_SIZE_TOKENS,
|
|
48
|
+
...INPUT_SIZE_TOKENS,
|
|
49
|
+
...SCREEN_SIZE_TOKENS,
|
|
50
|
+
].sort();
|
|
51
|
+
expect(declared).toEqual(registered);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("declare exactly the same button text tokens", () => {
|
|
55
|
+
const declared = [...TOKENS.keys()]
|
|
56
|
+
.filter((name) => name.startsWith("text-button-"))
|
|
57
|
+
.map((name) => name.replace("text-", ""))
|
|
58
|
+
.sort();
|
|
59
|
+
expect(declared).toEqual([...BUTTON_TEXT_TOKENS].sort());
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("declare exactly the same button radius tokens", () => {
|
|
63
|
+
const declared = [...TOKENS.keys()]
|
|
64
|
+
.filter((name) => name.startsWith("radius-button-"))
|
|
65
|
+
.map((name) => name.replace("radius-", ""))
|
|
66
|
+
.sort();
|
|
67
|
+
expect(declared).toEqual([...BUTTON_RADIUS_TOKENS].sort());
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("declare exactly the same input text tokens", () => {
|
|
71
|
+
const declared = [...TOKENS.keys()]
|
|
72
|
+
.filter((name) => name.startsWith("text-input-"))
|
|
73
|
+
.map((name) => name.replace("text-", ""))
|
|
74
|
+
.sort();
|
|
75
|
+
expect(declared).toEqual([...INPUT_TEXT_TOKENS].sort());
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("the chart scale", () => {
|
|
80
|
+
test("ascends in the order the registry lists it", () => {
|
|
81
|
+
const values = CHART_SIZE_TOKENS.map((token) => px(`spacing-${token}`));
|
|
82
|
+
expect(values).toEqual([...values].sort((a, b) => a - b));
|
|
83
|
+
expect(new Set(values).size).toBe(CHART_SIZE_TOKENS.length);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("stays taller than a control, since a chart is a region and not a row", () => {
|
|
87
|
+
// A chart shorter than an input would have no room for an axis under its
|
|
88
|
+
// own plot, and the labels would collide with the marks.
|
|
89
|
+
expect(px("spacing-chart-sm")).toBeGreaterThan(px("spacing-input-lg"));
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("the icon scale", () => {
|
|
94
|
+
// Component tests compare tokens by their index in ICON_SIZE_TOKENS rather
|
|
95
|
+
// than by looking up points, which only means anything while the array is
|
|
96
|
+
// ordered. This is the test that keeps that true.
|
|
97
|
+
test("ascends in the order the registry lists it", () => {
|
|
98
|
+
const values = ICON_SIZE_TOKENS.map((token) => px(`spacing-${token}`));
|
|
99
|
+
expect(values).toEqual([...values].sort((a, b) => a - b));
|
|
100
|
+
expect(new Set(values).size).toBe(ICON_SIZE_TOKENS.length);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("the button scale", () => {
|
|
105
|
+
test("ascends in the order the registry lists it", () => {
|
|
106
|
+
const values = BUTTON_SIZE_TOKENS.map((token) => px(`spacing-${token}`));
|
|
107
|
+
expect(values).toEqual([...values].sort((a, b) => a - b));
|
|
108
|
+
expect(new Set(values).size).toBe(BUTTON_SIZE_TOKENS.length);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("pairs each height with a label size and an icon that fit inside it", () => {
|
|
112
|
+
for (const [index, token] of BUTTON_SIZE_TOKENS.entries()) {
|
|
113
|
+
const height = px(`spacing-${token}`);
|
|
114
|
+
expect(px(`text-${BUTTON_TEXT_TOKENS[index]}`)).toBeLessThan(height);
|
|
115
|
+
// A button's icon indexes the shared scale at the same step name.
|
|
116
|
+
expect(px(`spacing-icon-${token.replace("button-", "")}`)).toBeLessThan(height);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("scales the label with the height", () => {
|
|
121
|
+
const text = BUTTON_TEXT_TOKENS.map((token) => px(`text-${token}`));
|
|
122
|
+
expect(text).toEqual([...text].sort((a, b) => a - b));
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// A radius past half the height is silently clamped to half by the
|
|
126
|
+
// renderer, so a number above it would stop meaning what it says — and the
|
|
127
|
+
// step it sits at would stop being retunable. Equal to half, which is what
|
|
128
|
+
// every step is today, draws the capsule the button ships as.
|
|
129
|
+
test("gives each height a corner it can actually draw", () => {
|
|
130
|
+
for (const [index, token] of BUTTON_SIZE_TOKENS.entries()) {
|
|
131
|
+
const height = px(`spacing-${token}`);
|
|
132
|
+
expect(px(`radius-${BUTTON_RADIUS_TOKENS[index]}`)).toBeLessThanOrEqual(height / 2);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("scales the corner with the height", () => {
|
|
137
|
+
const radii = BUTTON_RADIUS_TOKENS.map((token) => px(`radius-${token}`));
|
|
138
|
+
expect(radii).toEqual([...radii].sort((a, b) => a - b));
|
|
139
|
+
expect(new Set(radii).size).toBe(BUTTON_RADIUS_TOKENS.length);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// The three scales are indexed by the same step name — `button-md` names a
|
|
143
|
+
// height, a label size and a corner — so a step added to one and not the
|
|
144
|
+
// others would compile to a class with nothing behind it.
|
|
145
|
+
test("names its steps the same way across all three scales", () => {
|
|
146
|
+
expect([...BUTTON_RADIUS_TOKENS]).toEqual([...BUTTON_SIZE_TOKENS]);
|
|
147
|
+
expect([...BUTTON_TEXT_TOKENS]).toEqual([...BUTTON_SIZE_TOKENS]);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe("the input scale", () => {
|
|
152
|
+
test("ascends in the order the registry lists it", () => {
|
|
153
|
+
const values = INPUT_SIZE_TOKENS.map((token) => px(`spacing-${token}`));
|
|
154
|
+
expect(values).toEqual([...values].sort((a, b) => a - b));
|
|
155
|
+
expect(new Set(values).size).toBe(INPUT_SIZE_TOKENS.length);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("pairs each height with a text size and an icon that fit inside it", () => {
|
|
159
|
+
for (const [index, token] of INPUT_SIZE_TOKENS.entries()) {
|
|
160
|
+
const height = px(`spacing-${token}`);
|
|
161
|
+
expect(px(`text-${INPUT_TEXT_TOKENS[index]}`)).toBeLessThan(height);
|
|
162
|
+
// A decorator's icon indexes the shared scale at the same step name.
|
|
163
|
+
expect(px(`spacing-icon-${token.replace("input-", "")}`)).toBeLessThan(height);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("scales the text with the height", () => {
|
|
168
|
+
const text = INPUT_TEXT_TOKENS.map((token) => px(`text-${token}`));
|
|
169
|
+
expect(text).toEqual([...text].sort((a, b) => a - b));
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// `Input` and `Button` are the two controls a form puts side by side, so a
|
|
173
|
+
// field that stood a step taller than the button beside it would read as a
|
|
174
|
+
// mistake. They are separate scales precisely so this is asserted rather
|
|
175
|
+
// than assumed — see the note in tokens.css.
|
|
176
|
+
test("stands exactly as tall as the button beside it", () => {
|
|
177
|
+
for (const [index, token] of INPUT_SIZE_TOKENS.entries()) {
|
|
178
|
+
expect(px(`spacing-${token}`)).toBe(px(`spacing-${BUTTON_SIZE_TOKENS[index]}`));
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Names of the semantic size tokens defined in `tokens.css`.
|
|
3
|
+
*
|
|
4
|
+
* These exist so `cn()` can teach tailwind-merge about them. Without that,
|
|
5
|
+
* `h-button-md` is an unrecognised utility and twMerge will not drop it when a
|
|
6
|
+
* caller passes `h-12` — both survive and the override silently stops working.
|
|
7
|
+
* {@link ../lib/cn} registers every name here, and a test asserts this list
|
|
8
|
+
* still matches `tokens.css`.
|
|
9
|
+
*
|
|
10
|
+
* Values live in the CSS, not here. A component references a token by writing
|
|
11
|
+
* the class out in full (`size-icon-md`) — Tailwind scans source text, so a
|
|
12
|
+
* name assembled at runtime would never be compiled.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Suffixes in the `--spacing-*` namespace: `h-button-md`, `w-button-md`. */
|
|
16
|
+
export const BUTTON_SIZE_TOKENS = ["button-sm", "button-md", "button-lg"] as const;
|
|
17
|
+
|
|
18
|
+
/** Suffixes in the `--spacing-*` namespace: `size-icon-md`. */
|
|
19
|
+
export const ICON_SIZE_TOKENS = ["icon-xs", "icon-sm", "icon-md", "icon-lg", "icon-xl", "icon-2xl"] as const;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Suffixes in the `--spacing-*` namespace: `h-navbar-row`, `px-screen-gutter`.
|
|
23
|
+
*
|
|
24
|
+
* Not a scale — two independent measurements of a screen's chrome — so unlike
|
|
25
|
+
* the icon and button tokens their order carries no meaning.
|
|
26
|
+
*/
|
|
27
|
+
export const SCREEN_SIZE_TOKENS = ["navbar-row", "screen-gutter"] as const;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Suffixes in the `--spacing-*` namespace: `h-input-md`, `min-h-input-md`.
|
|
31
|
+
*
|
|
32
|
+
* Read on two axes, unlike the button's: a single-line field takes the height
|
|
33
|
+
* as a fixed one and a multiline field takes it as a floor, so both `h-` and
|
|
34
|
+
* `min-h-` have to be recognised.
|
|
35
|
+
*/
|
|
36
|
+
export const INPUT_SIZE_TOKENS = ["input-sm", "input-md", "input-lg"] as const;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Suffixes in the `--radius-*` namespace: `rounded-button-md`.
|
|
40
|
+
*
|
|
41
|
+
* Stepped with the button's heights rather than borrowing the generic
|
|
42
|
+
* `--radius-*` scale, so the corner is retuned with the control it belongs to
|
|
43
|
+
* and a caller's `rounded-lg` still overrides it.
|
|
44
|
+
*/
|
|
45
|
+
export const BUTTON_RADIUS_TOKENS = ["button-sm", "button-md", "button-lg"] as const;
|
|
46
|
+
|
|
47
|
+
/** Suffixes in the `--text-*` namespace: `text-button-md`. */
|
|
48
|
+
export const BUTTON_TEXT_TOKENS = ["button-sm", "button-md", "button-lg"] as const;
|
|
49
|
+
|
|
50
|
+
/** Suffixes in the `--text-*` namespace: `text-input-md`. */
|
|
51
|
+
export const INPUT_TEXT_TOKENS = ["input-sm", "input-md", "input-lg"] as const;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Suffixes in the `--spacing-*` namespace: `h-chart-md`.
|
|
55
|
+
*
|
|
56
|
+
* A chart canvas has no intrinsic height, so one is named here rather than
|
|
57
|
+
* written as a `h-56` at each call site — a dashboard's rows only line up if
|
|
58
|
+
* every chart agrees, and they only stay agreed if there is one number.
|
|
59
|
+
*/
|
|
60
|
+
export const CHART_SIZE_TOKENS = ["chart-sm", "chart-md", "chart-lg"] as const;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The tailwind-merge extension both mergers in this package are built from.
|
|
64
|
+
*
|
|
65
|
+
* There are two, and they are easy to forget: `cn()` merges a caller's
|
|
66
|
+
* className, and `tv()` merges slots and variants using a tailwind-merge
|
|
67
|
+
* instance of its own. Extending only one leaves the other treating
|
|
68
|
+
* `text-button-md` as an unrecognised utility — which is how a size token ends
|
|
69
|
+
* up colliding with a colour and silently dropping it. One config, read by
|
|
70
|
+
* `lib/cn.ts` and `lib/tv.ts`, so they cannot drift apart.
|
|
71
|
+
*/
|
|
72
|
+
export const TW_MERGE_CONFIG = {
|
|
73
|
+
extend: {
|
|
74
|
+
theme: {
|
|
75
|
+
radius: [...BUTTON_RADIUS_TOKENS],
|
|
76
|
+
spacing: [
|
|
77
|
+
...BUTTON_SIZE_TOKENS,
|
|
78
|
+
...CHART_SIZE_TOKENS,
|
|
79
|
+
...ICON_SIZE_TOKENS,
|
|
80
|
+
...INPUT_SIZE_TOKENS,
|
|
81
|
+
...SCREEN_SIZE_TOKENS,
|
|
82
|
+
],
|
|
83
|
+
text: [...BUTTON_TEXT_TOKENS, ...INPUT_TEXT_TOKENS],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
} as const;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="uniwind/types" />
|