@happyvertical/smrt-ui 0.55.16 → 0.55.18
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/AGENTS.md +14 -0
- package/README.md +75 -0
- package/dist/components/calendar/Calendar.svelte +59 -56
- package/dist/components/calendar/CalendarView.svelte +80 -77
- package/dist/components/calendar/DayView.svelte +39 -36
- package/dist/components/chat/MessageBubble.svelte +26 -23
- package/dist/components/chat/ReactionPicker.svelte +12 -9
- package/dist/components/chat/TypingIndicator.svelte +12 -9
- package/dist/components/data/CollectionList.svelte +11 -8
- package/dist/components/data/CollectionToolbar.svelte +5 -2
- package/dist/components/data/DataTable.svelte +74 -59
- package/dist/components/data/ListSortSelect.svelte +8 -5
- package/dist/components/data/SortableHeader.svelte +10 -7
- package/dist/components/display/ConfidenceBadge.svelte +11 -8
- package/dist/components/display/CurrencyDisplay.svelte +7 -4
- package/dist/components/display/DateDisplay.svelte +6 -3
- package/dist/components/display/Icon.svelte +4 -1
- package/dist/components/display/StatusBadge.svelte +9 -6
- package/dist/components/feedback/Alert.svelte +12 -8
- package/dist/components/feedback/ConfirmDialog.svelte +13 -10
- package/dist/components/feedback/LoadingOverlay.svelte +37 -34
- package/dist/components/feedback/Meter.svelte +4 -1
- package/dist/components/feedback/Modal.svelte +26 -21
- package/dist/components/feedback/PhoneSheet.svelte +6 -3
- package/dist/components/feedback/Progress.svelte +8 -5
- package/dist/components/feedback/ProgressBar.svelte +13 -10
- package/dist/components/feedback/Spinner.svelte +4 -1
- package/dist/components/feedback/ToastViewport.svelte +18 -15
- package/dist/components/feedback/WorkingStrip.svelte +6 -3
- package/dist/components/forms/CameraCapture.svelte +8 -5
- package/dist/components/forms/Checkbox.svelte +11 -8
- package/dist/components/forms/Combobox.svelte +5 -2
- package/dist/components/forms/DatePicker.svelte +7 -2
- package/dist/components/forms/DictationButton.svelte +17 -14
- package/dist/components/forms/DictationStatus.svelte +12 -9
- package/dist/components/forms/ErrorSummary.svelte +4 -1
- package/dist/components/forms/FieldLabel.svelte +9 -6
- package/dist/components/forms/Fieldset.svelte +4 -1
- package/dist/components/forms/FilePicker.svelte +5 -2
- package/dist/components/forms/Form.svelte +1 -1
- package/dist/components/forms/FormActionBar.svelte +4 -1
- package/dist/components/forms/FormGroup.svelte +6 -3
- package/dist/components/forms/Input.svelte +22 -15
- package/dist/components/forms/InputGroup.svelte +6 -3
- package/dist/components/forms/Listbox.svelte +5 -2
- package/dist/components/forms/MultiSelect.svelte +4 -1
- package/dist/components/forms/Radio.svelte +9 -6
- package/dist/components/forms/RadioGroup.svelte +5 -2
- package/dist/components/forms/RangeSlider.svelte +5 -2
- package/dist/components/forms/RelationInput.svelte +7 -4
- package/dist/components/forms/SearchInput.svelte +7 -4
- package/dist/components/forms/SegmentedControl.svelte +6 -3
- package/dist/components/forms/Select.svelte +20 -13
- package/dist/components/forms/SignaturePad.svelte +8 -5
- package/dist/components/forms/Slider.svelte +5 -2
- package/dist/components/forms/StagedControlReview.svelte +15 -12
- package/dist/components/forms/Switch.svelte +8 -5
- package/dist/components/forms/TagsInput.svelte +5 -2
- package/dist/components/forms/Textarea.svelte +20 -13
- package/dist/components/forms/TimePicker.svelte +7 -2
- package/dist/components/forms/Toggle.svelte +14 -11
- package/dist/components/forms/ToggleButton.svelte +5 -2
- package/dist/components/layout/ActionGroup.svelte +4 -1
- package/dist/components/layout/Container.svelte +4 -1
- package/dist/components/layout/EmptyState.svelte +11 -8
- package/dist/components/layout/Footer.svelte +4 -1
- package/dist/components/layout/Grid.svelte +13 -10
- package/dist/components/layout/Header.svelte +4 -1
- package/dist/components/layout/Masthead.svelte +4 -1
- package/dist/components/layout/PageHeader.svelte +12 -9
- package/dist/components/layout/PageLayout.svelte +3 -0
- package/dist/components/layout/SummaryCard.svelte +9 -6
- package/dist/components/memberships/MembershipCard.svelte +5 -2
- package/dist/components/memberships/MembershipList.svelte +5 -2
- package/dist/components/nav/FilterChips.svelte +13 -10
- package/dist/components/nav/Tabs.svelte +75 -41
- package/dist/components/permissions/PermissionPicker.svelte +5 -2
- package/dist/components/roles/RoleBadge.svelte +8 -5
- package/dist/components/roles/RoleSelector.svelte +22 -19
- package/dist/components/theme/ThemeProvider.svelte +7 -4
- package/dist/components/ui/Accordion.svelte +4 -1
- package/dist/components/ui/AccordionItem.svelte +5 -2
- package/dist/components/ui/Avatar.svelte +10 -7
- package/dist/components/ui/Badge.svelte +6 -2
- package/dist/components/ui/Button.svelte +49 -37
- package/dist/components/ui/Card.svelte +18 -14
- package/dist/components/ui/Chip.svelte +41 -14
- package/dist/components/ui/Disclosure.svelte +5 -2
- package/dist/components/ui/Dropdown.svelte +33 -25
- package/dist/components/ui/IconToggle.svelte +12 -9
- package/dist/components/ui/IconToggle.svelte.d.ts +1 -1
- package/dist/components/ui/IconToggle.svelte.d.ts.map +1 -1
- package/dist/components/ui/IconToggleGroup.svelte +8 -5
- package/dist/components/ui/Pagination.svelte +11 -8
- package/dist/components/ui/Popover.svelte +9 -6
- package/dist/components/ui/Skeleton.svelte +10 -7
- package/dist/components/ui/Tooltip.svelte +13 -9
- package/dist/components/ui/Tree.svelte +12 -9
- package/dist/styles/layers.css +8 -0
- package/dist/styles/layers.d.ts +9 -0
- package/dist/styles/layers.d.ts.map +1 -0
- package/dist/styles/layers.js +8 -0
- package/dist/styles/tokens.css +27 -69
- package/dist/styles/unstyled.css +65 -0
- package/dist/svelte/playground/BaseControlsPreview.svelte +3 -0
- package/dist/svelte/playground/CalendarPreview.svelte +5 -2
- package/dist/svelte/playground/CapturePreview.svelte +3 -0
- package/dist/svelte/playground/CodeSelectorsPreview.svelte +3 -0
- package/dist/svelte/playground/CollectionsPreview.svelte +3 -0
- package/dist/svelte/playground/DataTablePreview.svelte +3 -0
- package/dist/svelte/playground/FeedbackPreview.svelte +3 -0
- package/dist/svelte/playground/InteractiveControlsPreview.svelte +3 -0
- package/dist/svelte/playground/StylelessPreview.svelte +110 -0
- package/dist/svelte/playground/StylelessPreview.svelte.d.ts +4 -0
- package/dist/svelte/playground/StylelessPreview.svelte.d.ts.map +1 -0
- package/dist/svelte/playground.d.ts.map +1 -1
- package/dist/svelte/playground.js +10 -0
- package/dist/test-support/setup.js +9 -0
- package/dist/theme/ThemeProvider.svelte +3 -0
- package/dist/themes/CUSTOM_THEME_GUIDE.md +40 -0
- package/dist/themes/README.md +151 -0
- package/dist/themes/ThemeProvider.svelte +181 -12
- package/dist/themes/ThemeProvider.svelte.d.ts +7 -2
- package/dist/themes/ThemeProvider.svelte.d.ts.map +1 -1
- package/dist/themes/components/ColorSchemeToggle.svelte +17 -14
- package/dist/themes/components/ThemeSwitcher.svelte +14 -11
- package/dist/themes/create-theme.d.ts +22 -1
- package/dist/themes/create-theme.d.ts.map +1 -1
- package/dist/themes/create-theme.js +100 -9
- package/dist/themes/css-generator.d.ts.map +1 -1
- package/dist/themes/css-generator.js +20 -20
- package/dist/themes/derive.d.ts +31 -0
- package/dist/themes/derive.d.ts.map +1 -0
- package/dist/themes/derive.js +183 -0
- package/dist/themes/glass/index.d.ts.map +1 -1
- package/dist/themes/glass/index.js +127 -14
- package/dist/themes/happyvertical/index.d.ts.map +1 -1
- package/dist/themes/happyvertical/index.js +101 -20
- package/dist/themes/index.d.ts +2 -2
- package/dist/themes/index.d.ts.map +1 -1
- package/dist/themes/index.js +1 -1
- package/dist/themes/material/index.d.ts.map +1 -1
- package/dist/themes/material/index.js +129 -28
- package/dist/themes/required-tokens.generated.json +201 -0
- package/dist/themes/shared.d.ts +35 -0
- package/dist/themes/shared.d.ts.map +1 -1
- package/dist/themes/shared.js +32 -0
- package/dist/themes/smrt/index.d.ts +3 -0
- package/dist/themes/smrt/index.d.ts.map +1 -1
- package/dist/themes/smrt/index.js +108 -26
- package/dist/themes/studio/index.d.ts.map +1 -1
- package/dist/themes/studio/index.js +116 -40
- package/dist/themes/styles/base.css +229 -1
- package/dist/themes/styles/glass.css +107 -20
- package/dist/themes/styles/happyvertical.css +111 -15
- package/dist/themes/styles/material.css +127 -35
- package/dist/themes/styles/smrt.css +126 -31
- package/dist/themes/styles/studio.css +132 -37
- package/dist/themes/theme-script.d.ts +3 -1
- package/dist/themes/theme-script.d.ts.map +1 -1
- package/dist/themes/theme-script.js +7 -2
- package/dist/themes/types.d.ts +118 -6
- package/dist/themes/types.d.ts.map +1 -1
- package/dist/utils/theme/typography.d.ts +21 -21
- package/dist/utils/theme/typography.js +21 -21
- package/package.json +8 -2
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Alert from '../../components/feedback/Alert.svelte';
|
|
3
|
+
import Input from '../../components/forms/Input.svelte';
|
|
4
|
+
import Select from '../../components/forms/Select.svelte';
|
|
5
|
+
import Tabs from '../../components/nav/Tabs.svelte';
|
|
6
|
+
import Badge from '../../components/ui/Badge.svelte';
|
|
7
|
+
import Button from '../../components/ui/Button.svelte';
|
|
8
|
+
import Card from '../../components/ui/Card.svelte';
|
|
9
|
+
import Chip from '../../components/ui/Chip.svelte';
|
|
10
|
+
import unstyledCss from '../../styles/unstyled.css?inline';
|
|
11
|
+
|
|
12
|
+
// Native checkbox, outside the catalogue, so the toggle itself is never stripped.
|
|
13
|
+
let styleless = $state(false);
|
|
14
|
+
let active = $state('overview');
|
|
15
|
+
|
|
16
|
+
// Svelte does not interpolate text inside a style element, so the stylesheet is
|
|
17
|
+
// appended to the document head imperatively while the toggle is on.
|
|
18
|
+
$effect(() => {
|
|
19
|
+
if (!styleless) return;
|
|
20
|
+
const style = document.createElement('style');
|
|
21
|
+
style.setAttribute('data-smrt-styleless', '');
|
|
22
|
+
style.textContent = unstyledCss;
|
|
23
|
+
document.head.appendChild(style);
|
|
24
|
+
return () => style.remove();
|
|
25
|
+
});
|
|
26
|
+
const tabs = [
|
|
27
|
+
{ id: 'overview', label: 'Overview' },
|
|
28
|
+
{ id: 'activity', label: 'Activity' },
|
|
29
|
+
{ id: 'settings', label: 'Settings' },
|
|
30
|
+
];
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
<div class="workbench">
|
|
36
|
+
<header>
|
|
37
|
+
<div>
|
|
38
|
+
<p class="eyebrow">Styleless mode</p>
|
|
39
|
+
<h4>Layout kept, look removed</h4>
|
|
40
|
+
</div>
|
|
41
|
+
<label class="toggle">
|
|
42
|
+
<input type="checkbox" bind:checked={styleless} />
|
|
43
|
+
Apply unstyled.css
|
|
44
|
+
</label>
|
|
45
|
+
</header>
|
|
46
|
+
|
|
47
|
+
<p class="note">
|
|
48
|
+
With the toggle on, every component below keeps its layout and loses its
|
|
49
|
+
themed look. The page chrome is not a smrt component, so it is unaffected.
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
<div class="catalogue">
|
|
53
|
+
<section>
|
|
54
|
+
<h5>Buttons and badges</h5>
|
|
55
|
+
<div class="row">
|
|
56
|
+
<Button>Primary</Button>
|
|
57
|
+
<Button variant="secondary">Secondary</Button>
|
|
58
|
+
<Badge>Draft</Badge>
|
|
59
|
+
<Chip label="Filter" />
|
|
60
|
+
</div>
|
|
61
|
+
</section>
|
|
62
|
+
|
|
63
|
+
<section>
|
|
64
|
+
<h5>Card</h5>
|
|
65
|
+
<Card>
|
|
66
|
+
{#snippet header()}<strong>Workspace</strong>{/snippet}
|
|
67
|
+
<p>Card body text keeps its spacing and flow.</p>
|
|
68
|
+
{#snippet footer()}<Button size="sm">Open</Button>{/snippet}
|
|
69
|
+
</Card>
|
|
70
|
+
</section>
|
|
71
|
+
|
|
72
|
+
<section>
|
|
73
|
+
<h5>Alert</h5>
|
|
74
|
+
<Alert title="Information">The index refreshes in the background.</Alert>
|
|
75
|
+
</section>
|
|
76
|
+
|
|
77
|
+
<section>
|
|
78
|
+
<h5>Form controls</h5>
|
|
79
|
+
<div class="row">
|
|
80
|
+
<Input name="styleless-name" placeholder="Name" />
|
|
81
|
+
<Select name="styleless-role">
|
|
82
|
+
<option value="owner">Owner</option>
|
|
83
|
+
<option value="member">Member</option>
|
|
84
|
+
</Select>
|
|
85
|
+
</div>
|
|
86
|
+
</section>
|
|
87
|
+
|
|
88
|
+
<section>
|
|
89
|
+
<h5>Tabs</h5>
|
|
90
|
+
<Tabs {tabs} {active} onchange={(id) => (active = id)} />
|
|
91
|
+
</section>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<style>
|
|
96
|
+
@layer smrt.reset, smrt.structure, smrt.theme;
|
|
97
|
+
@layer smrt.theme {
|
|
98
|
+
.workbench { display: grid; gap: var(--smrt-spacing-6); color: var(--smrt-color-on-surface); }
|
|
99
|
+
header { display: flex; align-items: center; justify-content: space-between; gap: var(--smrt-spacing-3); padding-bottom: var(--smrt-spacing-4); border-bottom: 1px solid var(--smrt-color-outline-variant); }
|
|
100
|
+
h4, h5, p { margin: 0; }
|
|
101
|
+
h4 { font: var(--smrt-typography-headline-small-font); }
|
|
102
|
+
h5 { margin-bottom: var(--smrt-spacing-3); font: var(--smrt-typography-title-medium-font); }
|
|
103
|
+
.eyebrow { color: var(--smrt-color-primary); font: var(--smrt-typography-label-small-font); letter-spacing: .1em; text-transform: uppercase; }
|
|
104
|
+
.note { color: var(--smrt-color-on-surface-variant); font: var(--smrt-typography-body-medium-font); }
|
|
105
|
+
.toggle { display: inline-flex; align-items: center; gap: var(--smrt-spacing-2); }
|
|
106
|
+
.catalogue { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: var(--smrt-spacing-5); }
|
|
107
|
+
.catalogue > section { display: grid; align-content: start; gap: var(--smrt-spacing-2); }
|
|
108
|
+
.row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--smrt-spacing-3); }
|
|
109
|
+
}
|
|
110
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StylelessPreview.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/playground/StylelessPreview.svelte.ts"],"names":[],"mappings":"AA+GA,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../src/svelte/playground.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../src/svelte/playground.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAaA,wBAmHE"}
|
|
@@ -6,6 +6,7 @@ const loadCollections = () => import('./playground/CollectionsPreview.svelte');
|
|
|
6
6
|
const loadCalendar = () => import('./playground/CalendarPreview.svelte');
|
|
7
7
|
const loadCapture = () => import('./playground/CapturePreview.svelte');
|
|
8
8
|
const loadPermissionPicker = () => import('./playground/PermissionPickerPreview.svelte');
|
|
9
|
+
const loadStyleless = () => import('./playground/StylelessPreview.svelte');
|
|
9
10
|
export default {
|
|
10
11
|
packageName: '@happyvertical/smrt-ui',
|
|
11
12
|
displayName: 'UI Foundation',
|
|
@@ -100,5 +101,14 @@ export default {
|
|
|
100
101
|
tags: ['forms', 'camera', 'signature', 'files'],
|
|
101
102
|
modes: { mock: { label: 'Interactive' } },
|
|
102
103
|
},
|
|
104
|
+
{
|
|
105
|
+
id: 'styleless-mode',
|
|
106
|
+
title: 'Styleless Mode',
|
|
107
|
+
description: 'Toggle unstyled.css across the catalogue: components keep layout and drop their themed look, so consumer CSS can restyle them.',
|
|
108
|
+
loadComponent: loadStyleless,
|
|
109
|
+
order: 10,
|
|
110
|
+
tags: ['styling', 'styleless', 'layers', 'restyling'],
|
|
111
|
+
modes: { mock: { label: 'Interactive' } },
|
|
112
|
+
},
|
|
103
113
|
],
|
|
104
114
|
};
|
|
@@ -40,3 +40,12 @@ if (typeof HTMLDialogElement !== 'undefined') {
|
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
+
// jsdom has no Web Animations API. Svelte's `animate:` directive (list FLIP)
|
|
44
|
+
// and transitions call `element.getAnimations()`; an empty list is accurate
|
|
45
|
+
// here because jsdom never runs CSS animations.
|
|
46
|
+
if (typeof Element !== 'undefined' &&
|
|
47
|
+
typeof Element.prototype.getAnimations !== 'function') {
|
|
48
|
+
Element.prototype.getAnimations = function getAnimations() {
|
|
49
|
+
return [];
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -200,8 +200,11 @@ $effect(() => {
|
|
|
200
200
|
</div>
|
|
201
201
|
|
|
202
202
|
<style>
|
|
203
|
+
@layer smrt.reset, smrt.structure, smrt.theme;
|
|
204
|
+
@layer smrt.theme {
|
|
203
205
|
.smrt-theme-root {
|
|
204
206
|
/* Ensure theme variables cascade to all children */
|
|
205
207
|
display: contents;
|
|
206
208
|
}
|
|
209
|
+
}
|
|
207
210
|
</style>
|
|
@@ -318,6 +318,46 @@ npm install ./packages/my-brand-theme
|
|
|
318
318
|
<ThemeSwitcher variant="segmented" />
|
|
319
319
|
```
|
|
320
320
|
|
|
321
|
+
## Required Tokens
|
|
322
|
+
|
|
323
|
+
A theme is complete only when it defines every token the components consume. The list below is generated from the `var(--smrt-*)` references in the packages' `.svelte` files; `src/themes/__tests__/required-tokens.test.ts` asserts every preset and `createTheme()` output defines each one.
|
|
324
|
+
|
|
325
|
+
<!-- smrt-required-tokens:start -->
|
|
326
|
+
|
|
327
|
+
### Required tokens (generated)
|
|
328
|
+
|
|
329
|
+
Every theme (presets and `createTheme()` output) must define these names. Generated by
|
|
330
|
+
`scripts/generate-smrt-required-tokens.mjs`; do not edit by hand. Component-private
|
|
331
|
+
`--smrt-<component>-*` knobs are excluded.
|
|
332
|
+
|
|
333
|
+
- **color** (40): `--smrt-color-background`, `--smrt-color-error`, `--smrt-color-error-container`, `--smrt-color-inverse-on-surface`, `--smrt-color-inverse-surface`, `--smrt-color-on-background`, `--smrt-color-on-error`, `--smrt-color-on-error-container`, `--smrt-color-on-primary`, `--smrt-color-on-primary-container`, `--smrt-color-on-secondary`, `--smrt-color-on-secondary-container`, `--smrt-color-on-success-container`, `--smrt-color-on-surface`, `--smrt-color-on-surface-variant`, `--smrt-color-on-tertiary`, `--smrt-color-on-tertiary-container`, `--smrt-color-on-warning-container`, `--smrt-color-outline`, `--smrt-color-outline-variant`, `--smrt-color-primary`, `--smrt-color-primary-container`, `--smrt-color-scrim`, `--smrt-color-secondary`, `--smrt-color-secondary-container`, `--smrt-color-shadow`, `--smrt-color-success`, `--smrt-color-success-container`, `--smrt-color-surface`, `--smrt-color-surface-container`, `--smrt-color-surface-container-high`, `--smrt-color-surface-container-highest`, `--smrt-color-surface-container-low`, `--smrt-color-surface-container-lowest`, `--smrt-color-surface-dim`, `--smrt-color-surface-variant`, `--smrt-color-tertiary`, `--smrt-color-tertiary-container`, `--smrt-color-warning`, `--smrt-color-warning-container`
|
|
334
|
+
- **control** (4): `--smrt-control-height`, `--smrt-control-height-small`, `--smrt-control-radius`, `--smrt-control-radius-small`
|
|
335
|
+
- **duration** (6): `--smrt-duration-fast`, `--smrt-duration-medium1`, `--smrt-duration-normal`, `--smrt-duration-short2`, `--smrt-duration-short3`, `--smrt-duration-short4`
|
|
336
|
+
- **easing** (3): `--smrt-easing-emphasized`, `--smrt-easing-emphasized-decelerate`, `--smrt-easing-standard`
|
|
337
|
+
- **elevation** (5): `--smrt-elevation-1`, `--smrt-elevation-2`, `--smrt-elevation-3`, `--smrt-elevation-4`, `--smrt-elevation-5`
|
|
338
|
+
- **focus** (3): `--smrt-focus-ring-color`, `--smrt-focus-ring-offset`, `--smrt-focus-ring-width`
|
|
339
|
+
- **font** (2): `--smrt-font-family`, `--smrt-font-family-mono`
|
|
340
|
+
- **glass** (4): `--smrt-glass-blur`, `--smrt-glass-saturation`, `--smrt-glass-surface`, `--smrt-glass-surface-strong`
|
|
341
|
+
- **hairline** (4): `--smrt-hairline`, `--smrt-hairline-color`, `--smrt-hairline-strong`, `--smrt-hairline-strong-color`
|
|
342
|
+
- **inset** (1): `--smrt-inset-highlight`
|
|
343
|
+
- **radius** (11): `--smrt-radius-2xl`, `--smrt-radius-extra-large`, `--smrt-radius-extra-small`, `--smrt-radius-full`, `--smrt-radius-large`, `--smrt-radius-lg`, `--smrt-radius-md`, `--smrt-radius-medium`, `--smrt-radius-sm`, `--smrt-radius-small`, `--smrt-radius-xl`
|
|
344
|
+
- **scrollbar** (2): `--smrt-scrollbar-thumb`, `--smrt-scrollbar-track`
|
|
345
|
+
- **spacing** (19): `--smrt-spacing-0_5`, `--smrt-spacing-1`, `--smrt-spacing-10`, `--smrt-spacing-11`, `--smrt-spacing-12`, `--smrt-spacing-1_5`, `--smrt-spacing-2`, `--smrt-spacing-2xl`, `--smrt-spacing-3`, `--smrt-spacing-3xl`, `--smrt-spacing-4`, `--smrt-spacing-5`, `--smrt-spacing-6`, `--smrt-spacing-8`, `--smrt-spacing-lg`, `--smrt-spacing-md`, `--smrt-spacing-sm`, `--smrt-spacing-xl`, `--smrt-spacing-xs`
|
|
346
|
+
- **state** (5): `--smrt-state-disabled`, `--smrt-state-dragged`, `--smrt-state-hover`, `--smrt-state-pressed`, `--smrt-state-selected`
|
|
347
|
+
- **surface** (6): `--smrt-surface-control`, `--smrt-surface-ground`, `--smrt-surface-overlay`, `--smrt-surface-panel`, `--smrt-surface-raised`, `--smrt-surface-sunken`
|
|
348
|
+
- **touch** (1): `--smrt-touch-target-min`
|
|
349
|
+
- **typography** (56): `--smrt-typography-body-large-font`, `--smrt-typography-body-large-line-height`, `--smrt-typography-body-large-size`, `--smrt-typography-body-large-tracking`, `--smrt-typography-body-medium-font`, `--smrt-typography-body-medium-line-height`, `--smrt-typography-body-medium-size`, `--smrt-typography-body-medium-tracking`, `--smrt-typography-body-medium-weight`, `--smrt-typography-body-small-font`, `--smrt-typography-body-small-line-height`, `--smrt-typography-body-small-size`, `--smrt-typography-body-small-weight`, `--smrt-typography-display-medium-line-height`, `--smrt-typography-display-medium-size`, `--smrt-typography-display-small-size`, `--smrt-typography-headline-large-line-height`, `--smrt-typography-headline-large-size`, `--smrt-typography-headline-large-weight`, `--smrt-typography-headline-medium-line-height`, `--smrt-typography-headline-medium-size`, `--smrt-typography-headline-medium-weight`, `--smrt-typography-headline-small-font`, `--smrt-typography-headline-small-line-height`, `--smrt-typography-headline-small-size`, `--smrt-typography-headline-small-weight`, `--smrt-typography-label-large-font`, `--smrt-typography-label-large-size`, `--smrt-typography-label-large-tracking`, `--smrt-typography-label-large-weight`, `--smrt-typography-label-medium-font`, `--smrt-typography-label-medium-size`, `--smrt-typography-label-medium-tracking`, `--smrt-typography-label-medium-weight`, `--smrt-typography-label-small-font`, `--smrt-typography-label-small-line-height`, `--smrt-typography-label-small-size`, `--smrt-typography-label-small-tracking`, `--smrt-typography-label-small-weight`, `--smrt-typography-title-large-font`, `--smrt-typography-title-large-line-height`, `--smrt-typography-title-large-size`, `--smrt-typography-title-medium-font`, `--smrt-typography-title-medium-font-family`, `--smrt-typography-title-medium-line-height`, `--smrt-typography-title-medium-size`, `--smrt-typography-title-medium-tracking`, `--smrt-typography-title-medium-weight`, `--smrt-typography-title-small-font`, `--smrt-typography-title-small-size`, `--smrt-typography-title-small-tracking`, `--smrt-typography-title-small-weight`, `--smrt-typography-weight-bold`, `--smrt-typography-weight-medium`, `--smrt-typography-weight-normal`, `--smrt-typography-weight-semibold`
|
|
350
|
+
- **z** (7): `--smrt-z-index-dialog`, `--smrt-z-index-dropdown`, `--smrt-z-index-modal`, `--smrt-z-index-overlay`, `--smrt-z-index-sticky`, `--smrt-z-index-toast`, `--smrt-z-index-tooltip`
|
|
351
|
+
|
|
352
|
+
<!-- smrt-required-tokens:end -->
|
|
353
|
+
|
|
354
|
+
## Runtime overrides
|
|
355
|
+
|
|
356
|
+
`ThemeProvider` layers user choices over a theme without touching the theme itself:
|
|
357
|
+
`borderRadius` (control radius tokens), `primaryColor` (full derived primary set, also
|
|
358
|
+
available as `derivePrimaryOverrides(color, scheme)`), `density` and `contrast`. See the
|
|
359
|
+
ThemeProvider props table in the README for the exact behaviour.
|
|
360
|
+
|
|
321
361
|
## Best Practices
|
|
322
362
|
|
|
323
363
|
1. **Extend a base theme**: Start with `extend: 'material'` or another base to get sensible defaults
|
package/dist/themes/README.md
CHANGED
|
@@ -74,6 +74,9 @@ const autoTheme = createThemeFromColor('#6366f1', 'indigo', 'Indigo Theme');
|
|
|
74
74
|
registerTheme(autoTheme);
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
Generated dark themes use tonal primary colors so primary text and icon
|
|
78
|
+
containers remain readable even when the brand seed is very dark.
|
|
79
|
+
|
|
77
80
|
### Full Custom Theme with All Options
|
|
78
81
|
|
|
79
82
|
```typescript
|
|
@@ -302,8 +305,29 @@ import '@happyvertical/smrt-ui/themes/styles/happyvertical.css';
|
|
|
302
305
|
| `borderRadius` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | `'md'` | Border radius scale |
|
|
303
306
|
| `overrides` | `Record<string, string>` | `{}` | Custom CSS variable overrides |
|
|
304
307
|
| `persist` | `boolean` | `true` | Persist to localStorage |
|
|
308
|
+
| `density` | `'comfortable' \| 'touch'` | - | Default control target size (`--smrt-control-target-min`); persisted |
|
|
309
|
+
| `contrast` | `'auto' \| 'standard' \| 'more'` | `'auto'` | `auto` follows `prefers-contrast: more`; persisted when explicit |
|
|
305
310
|
| `storageKey` | `string` | `'smrt-theme'` | localStorage key |
|
|
306
311
|
|
|
312
|
+
Behaviour notes (state exposed on context: `contrast`, `isChangingScheme`):
|
|
313
|
+
|
|
314
|
+
- `borderRadius` other than `md` is emitted inline as `--smrt-control-radius` /
|
|
315
|
+
`--smrt-control-radius-small` (one scale step smaller for the small radius);
|
|
316
|
+
`md` leaves the preset's own control radius in place. The generic
|
|
317
|
+
`--smrt-radius-*` scale is not changed.
|
|
318
|
+
- `primaryColor` emits the whole derived set for the resolved scheme through
|
|
319
|
+
`derivePrimaryOverrides(color, scheme)` (also exported): `color-primary`, `on-primary`
|
|
320
|
+
(best of white/black), `primary-container`, `on-primary-container`, `inverse-primary`,
|
|
321
|
+
and the focus ring, accent, caret and selection tokens.
|
|
322
|
+
- After the resolved scheme changes post-mount, `data-smrt-theme-changing` is set on
|
|
323
|
+
the root and `<html>` for one `--smrt-duration-scheme`, so styles can crossfade.
|
|
324
|
+
Never on first paint; skipped under `prefers-reduced-motion: reduce`.
|
|
325
|
+
- Contrast: `data-contrast="standard|more|forced"` on `<html>` and the root;
|
|
326
|
+
`forced-colors: active` also sets `data-forced-colors`. Contract for components in
|
|
327
|
+
forced-colors mode: do not paint over system colours, hairlines become `CanvasText`
|
|
328
|
+
and focus rings `Highlight` (component side: #3867 follow-up).
|
|
329
|
+
- `themeScript()` stamps `data-contrast` and (when persisted) `data-density` pre-paint.
|
|
330
|
+
|
|
307
331
|
## Theme Switching
|
|
308
332
|
|
|
309
333
|
Use the context to switch themes programmatically:
|
|
@@ -605,6 +629,133 @@ All themes expose CSS custom properties:
|
|
|
605
629
|
--smrt-easing-standard
|
|
606
630
|
```
|
|
607
631
|
|
|
632
|
+
### One vocabulary: `--smrt-*` only
|
|
633
|
+
|
|
634
|
+
`--smrt-*` is the only token namespace. Component CSS consumes these tokens and
|
|
635
|
+
nothing else; the legacy `--color-*`, `--spacing-*`, `--radius-*`, `--md-sys-*`
|
|
636
|
+
families were removed (issue #3822). Three rules follow:
|
|
637
|
+
|
|
638
|
+
- **Consume a token, don't invent one.** Use `var(--smrt-color-primary)` etc. Hard
|
|
639
|
+
colour or spacing literals belong in a theme, not a component.
|
|
640
|
+
- **A component-private variable is `--smrt-<component>-<name>`**, declared on the
|
|
641
|
+
component root (for example `--smrt-toast-viewport-inset`). A parent or app may set
|
|
642
|
+
it. It is not a theme token and should not be read from outside the component.
|
|
643
|
+
- **Overrides go through a theme or a documented hook**, never a `:global` rule or
|
|
644
|
+
`!important`. Unlayered app CSS still wins over the component's own styles.
|
|
645
|
+
|
|
646
|
+
`packages/smrt-ui/src/styles/tokens.css` defines the theme-invariant baseline only
|
|
647
|
+
(spacing, radius, duration, touch targets). Colour and typography come from a theme.
|
|
648
|
+
|
|
649
|
+
The lint gate `scripts/check-smrt-token-vocabulary.mjs` runs in `pnpm lint` and fails
|
|
650
|
+
on any custom property outside `--smrt-*` in `packages/*/src`. The one-time rewrite
|
|
651
|
+
was `scripts/codemod-smrt-tokens.mjs`; it is idempotent, so re-run it after a rebase
|
|
652
|
+
instead of hand-merging renamed names.
|
|
653
|
+
|
|
654
|
+
### Premium baseline token vocabulary
|
|
655
|
+
|
|
656
|
+
Every preset and every `createTheme()` result defines all of these in both schemes
|
|
657
|
+
(sections of `Theme` in `types.ts`; defaults derived in `derive.ts`; emitted by
|
|
658
|
+
`generateThemeVariables`). Themes and presets define them; **components only consume
|
|
659
|
+
them**. Do not redeclare a theme token on a component root (CSS or `style:`): the
|
|
660
|
+
required-tokens scanner (`scripts/smrt-token-scan-lib.mjs`) treats any `--smrt-*` name
|
|
661
|
+
a component defines on its own root as component-private and drops it from the required
|
|
662
|
+
list repo-wide. A per-variant override uses a component-prefixed name that falls back to
|
|
663
|
+
the theme token, for example `background: color-mix(in srgb, currentColor
|
|
664
|
+
calc(var(--smrt-button-state-hover, var(--smrt-state-hover)) * 100%), transparent)`.
|
|
665
|
+
|
|
666
|
+
| Token | Meaning | Consumed by |
|
|
667
|
+
| --- | --- | --- |
|
|
668
|
+
| `--smrt-surface-ground` | Page ground | `body`/shell background |
|
|
669
|
+
| `--smrt-surface-panel` | Cards, sidebars, panels on the ground | `background` of Card, sidebars, header, footer |
|
|
670
|
+
| `--smrt-surface-control` | Input and control fill | `background` of Input, Select, Textarea, Button (outlined/tonal) |
|
|
671
|
+
| `--smrt-surface-raised` | Menus, popovers, dialogs above panels | `background` of overlays |
|
|
672
|
+
| `--smrt-surface-sunken` | Wells and recessed regions | `background` of tables headers, code, inset areas |
|
|
673
|
+
| `--smrt-surface-overlay` | Modal scrim | `background` of Modal/Drawer backdrops |
|
|
674
|
+
| `--smrt-hairline-color` / `-strong-color` | Quiet and strong edge colours | `border-color`, `outline-color`, dividers |
|
|
675
|
+
| `--smrt-hairline` / `--smrt-hairline-strong` | `1px solid` edge shorthands | `border`, `border-top` etc. on panels (quiet) and controls (strong) |
|
|
676
|
+
| `--smrt-inset-highlight` | Top inner light line (`none` on light) | `box-shadow` (layer with an elevation) on raised dark surfaces |
|
|
677
|
+
| `--smrt-state-{hover,pressed,selected,focus,dragged,disabled}` | Unitless state-layer opacities, 0 to 1 | Mix percentage of a state overlay (`calc(var(--smrt-state-hover) * 100%)`); `disabled` as `opacity` |
|
|
678
|
+
| `--smrt-shadow-color` | `rgb(r g b)` shadow base, per scheme | `box-shadow` colour: `rgb(from var(--smrt-shadow-color) r g b / 0.2)` |
|
|
679
|
+
| `--smrt-elevation-0..5` | Existing shadow scale | `box-shadow` |
|
|
680
|
+
| `--smrt-control-radius` / `-radius-small` | Control corner radii | `border-radius` of controls |
|
|
681
|
+
| `--smrt-control-height` / `-height-small` | Control heights | `min-height`/`block-size` of controls |
|
|
682
|
+
| `--smrt-control-weight` | Control label weight | `font-weight` of buttons and inputs |
|
|
683
|
+
| `--smrt-control-border` | Control edge shorthand (`var(--smrt-hairline-strong)`) | `border` of controls |
|
|
684
|
+
| `--smrt-control-target-min` | Density-driven minimum hit size (set by ThemeProvider `density`) | `min-height`/`min-width` of interactive controls |
|
|
685
|
+
| `--smrt-control-hit-size` | Hit area of small icon controls | `inline-size`/`block-size` of icon buttons |
|
|
686
|
+
| `--smrt-focus-ring-{color,width,offset}` | Focus indicator | `outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color)`, `outline-offset` on `:focus-visible` |
|
|
687
|
+
| `--smrt-accent-color` | Native control accent | `accent-color` (checkbox, radio, range, date pickers) |
|
|
688
|
+
| `--smrt-selection-{background,color}` | Text selection | `::selection` |
|
|
689
|
+
| `--smrt-caret-color` | Text caret | `caret-color` |
|
|
690
|
+
| `--smrt-scrollbar-{thumb,track}` | Scrollbar colours | `scrollbar-color: thumb track` |
|
|
691
|
+
| `--smrt-glass-blur` / `-saturation` | Backdrop filter amounts (`0px`/`100%` outside Glass) | `backdrop-filter: blur(var(--smrt-glass-blur)) saturate(var(--smrt-glass-saturation))` |
|
|
692
|
+
| `--smrt-glass-surface` / `-surface-strong` | Translucent (Glass) or opaque surface fills; opacity is part of the colour | `background` of floating chrome and overlays |
|
|
693
|
+
| `--smrt-glass-border` | Glass edge colour | `border-color` of glass surfaces |
|
|
694
|
+
| `--smrt-duration-scheme` | Scheme crossfade duration | `transition-duration` when the colour scheme changes |
|
|
695
|
+
| `--smrt-font-feature-settings` | OpenType features (`"tnum" 1, "cv11" 1` on Inter presets) | `font-feature-settings` on the root and numeric cells |
|
|
696
|
+
|
|
697
|
+
#### Semantic info colour
|
|
698
|
+
|
|
699
|
+
`ColorPalette` carries an info family beside success, warning and error:
|
|
700
|
+
`--smrt-color-info`, `--smrt-color-on-info`, `--smrt-color-info-container` and
|
|
701
|
+
`--smrt-color-on-info-container`. Presets without a distinct information hue define
|
|
702
|
+
it as their tertiary family (Material, Glass, Studio, Day Shift); **smrt** uses a
|
|
703
|
+
steel blue (`#2c6a96` light, `#6fa8d6` dark) so Information no longer shares amber
|
|
704
|
+
with Warning. `createTheme()` follows a supplied `tertiary` unless `info` is also
|
|
705
|
+
given. Components that show information status (for example `Alert` `variant="info"`)
|
|
706
|
+
should consume these, not tertiary.
|
|
707
|
+
|
|
708
|
+
#### Preset identities on the baseline
|
|
709
|
+
|
|
710
|
+
| Preset | Dark ladder (ground / panel / control / raised) | Depth | State layers (hover / pressed / selected) | Controls |
|
|
711
|
+
| --- | --- | --- | --- | --- |
|
|
712
|
+
| Material | `#121316` / `#1b1c20` / `#26282c` / `#34363a` (M3 tonal, no borders needed; control border is transparent) | contact + ambient; dark adds the inset highlight | .08 / .12 / .12 | radius md / sm, 2.75rem |
|
|
713
|
+
| Glass | `#0b0b0f` ground; panel 72% and popover 88% frosted glass, input a recessed 28% black well; blur 20px, saturation 180% | softest, widest ambient | .10 / .14 / .14 | radius lg / md, 2.75rem |
|
|
714
|
+
| Studio | `#131416` / `#131416` / `#1c1d20` / `#292a2d` (flat, crisp hairlines, panel equals ground) | elevation 1 is the hairline; tight shadows | .06 / .10 / .10 | radius 0.375rem / sm, 2.5rem |
|
|
715
|
+
| smrt | `#0b0c0e` / `#121417` / `#181b1f` / `#1d2126` (instrument dark, amber signal, steel-blue info) | elevation 1 is the hairline; calm two-layer shadows (no far-spread) | .10 / .16 / .14 | radius sm, 2.5rem |
|
|
716
|
+
| Day Shift | enamel ground, faceplate panel, control a hair lighter, raised = faceplate; hairlines are the bezel | elevation 1 is the bezel hairline; minimal | .06 / .10 / .10 | radius md (6px) / sm, 2.5rem |
|
|
717
|
+
|
|
718
|
+
Elevation 2 to 5 are two-layer shadows built with `shadow()` in `shared.ts`: a contact
|
|
719
|
+
layer (`0 1px 2px`) plus an ambient layer (`0 Npx Mpx` with negative spread), both
|
|
720
|
+
`color-mix`ed from `--smrt-shadow-color`. Dark schemes prepend
|
|
721
|
+
`var(--smrt-inset-highlight)`. Weight is 500 on every preset. Native chrome follows the
|
|
722
|
+
baseline: accent and caret are primary, selection is the primary container, and the
|
|
723
|
+
scrollbar thumb is the strong hairline over a transparent track.
|
|
724
|
+
|
|
725
|
+
`--smrt-glass-border-opacity` and `--smrt-glass-background-opacity` are deprecated: still emitted (from `GlassEffects.borderOpacity`/`backgroundOpacity`, `1` when unset) for one release and removed in the next minor; the former
|
|
726
|
+
tokens, and the ThemeProvider-local `--smrt-glass-surface`/`--smrt-glass-border`,
|
|
727
|
+
were folded into the glass surface colours above.
|
|
728
|
+
|
|
729
|
+
### Typography
|
|
730
|
+
|
|
731
|
+
`themes/styles/base.css` (import once at the app entry) maps headings to type roles
|
|
732
|
+
inside `@layer smrt.structure`, scoped under `body` and `.smrt-theme-root`, so presets'
|
|
733
|
+
display faces reach plain markup. Hosts restyle with unlayered CSS.
|
|
734
|
+
|
|
735
|
+
| Element | Role |
|
|
736
|
+
| --- | --- |
|
|
737
|
+
| `h1` | `display-small` |
|
|
738
|
+
| `h2` | `headline-large` |
|
|
739
|
+
| `h3` | `headline-medium` |
|
|
740
|
+
| `h4` | `headline-small` |
|
|
741
|
+
| `h5` | `title-large` |
|
|
742
|
+
| `h6` | `title-medium` |
|
|
743
|
+
|
|
744
|
+
Each heading takes `font-family`, `font-size`, `line-height`, `font-weight` and
|
|
745
|
+
`letter-spacing` from the role's typography tokens (for example `--smrt-typography-headline-large-size`), and a `margin-block` from
|
|
746
|
+
`--smrt-spacing-*` (first children lose the top margin). Utilities
|
|
747
|
+
`.smrt-{display,headline,title,body,label}-{large,medium,small}` apply the same five
|
|
748
|
+
properties to any element.
|
|
749
|
+
|
|
750
|
+
Tabular figures: the root sets `font-feature-settings: var(--smrt-font-feature-settings)`,
|
|
751
|
+
and `table`, `time`, `[data-smrt-component="DataTable"]`, `[data-smrt-component="Badge"]`
|
|
752
|
+
and `.smrt-tabular` set `font-variant-numeric: tabular-nums`.
|
|
753
|
+
|
|
754
|
+
Preset rhythm: display tracking tightens with size (about -0.02em at display, -0.01em at
|
|
755
|
+
headline, 0 at title), labels weigh 500 (Day Shift keeps its documented bold B612 labels),
|
|
756
|
+
body is 400, and display plus headline roles use the preset's display face where it has one
|
|
757
|
+
(smrt: Space Grotesk; Day Shift: its documented stack).
|
|
758
|
+
|
|
608
759
|
## Advanced Usage
|
|
609
760
|
|
|
610
761
|
### Build-Time Theming
|