@dorsk/tsumikit 0.78.0 → 0.80.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/dist/components/atoms/Artwork.svelte +5 -3
- package/dist/components/atoms/Badge.svelte +2 -5
- package/dist/components/atoms/Button.svelte +327 -17
- package/dist/components/atoms/Button.svelte.d.ts +4 -1
- package/dist/components/atoms/Card.svelte +160 -12
- package/dist/components/atoms/Checkbox.svelte +181 -3
- package/dist/components/atoms/Gauge.svelte +2 -2
- package/dist/components/atoms/Input.svelte +41 -3
- package/dist/components/atoms/Kbd.svelte +1 -1
- package/dist/components/atoms/Link.svelte +61 -0
- package/dist/components/atoms/Progress.svelte +75 -2
- package/dist/components/atoms/Progress.svelte.d.ts +1 -0
- package/dist/components/atoms/SegmentedProgress.svelte +2 -2
- package/dist/components/atoms/Select.svelte +39 -3
- package/dist/components/atoms/Skeleton.svelte +54 -3
- package/dist/components/atoms/Skeleton.svelte.d.ts +1 -0
- package/dist/components/atoms/Slider.svelte +8 -6
- package/dist/components/atoms/Spinner.svelte +152 -3
- package/dist/components/atoms/Spinner.svelte.d.ts +1 -1
- package/dist/components/atoms/Swatch.svelte +1 -1
- package/dist/components/atoms/Switch.svelte +138 -2
- package/dist/components/atoms/Textarea.svelte +42 -3
- package/dist/components/atoms/Thinking.svelte +130 -0
- package/dist/components/atoms/Thinking.svelte.d.ts +15 -0
- package/dist/components/layouts/AppShell.svelte +6 -6
- package/dist/components/layouts/MasterDetail.svelte +3 -3
- package/dist/components/layouts/NavBar.svelte +4 -4
- package/dist/components/layouts/NavSection.svelte +1 -1
- package/dist/components/layouts/ResizablePanel.svelte +7 -7
- package/dist/components/layouts/TabBar.svelte +2 -2
- package/dist/components/molecules/Accordion.svelte +2 -2
- package/dist/components/molecules/AttachmentList.svelte +2 -2
- package/dist/components/molecules/CapBar.svelte +2 -2
- package/dist/components/molecules/CodeBlock.svelte +5 -5
- package/dist/components/molecules/Combobox.svelte +3 -3
- package/dist/components/molecules/CopyButton.svelte +146 -5
- package/dist/components/molecules/Drawer.svelte +8 -8
- package/dist/components/molecules/EmojiPicker.svelte +1 -1
- package/dist/components/molecules/Field.svelte +55 -1
- package/dist/components/molecules/FileButton.svelte +20 -6
- package/dist/components/molecules/FilterInput.svelte +4 -4
- package/dist/components/molecules/GitRef.svelte +2 -2
- package/dist/components/molecules/GlyphStack.svelte +21 -3
- package/dist/components/molecules/InputGroup.svelte +3 -3
- package/dist/components/molecules/LookPicker.svelte +87 -0
- package/dist/components/molecules/LookPicker.svelte.d.ts +17 -0
- package/dist/components/molecules/Modal.svelte +5 -5
- package/dist/components/molecules/MotionSettings.svelte +166 -0
- package/dist/components/molecules/MotionSettings.svelte.d.ts +18 -0
- package/dist/components/molecules/OptionButton.svelte +8 -3
- package/dist/components/molecules/Pagination.svelte +3 -1
- package/dist/components/molecules/Popover.svelte +18 -11
- package/dist/components/molecules/Prose.svelte +6 -6
- package/dist/components/molecules/RadialMenu.svelte +3 -3
- package/dist/components/molecules/RadioGroup.svelte +67 -4
- package/dist/components/molecules/RichSelect.svelte +6 -4
- package/dist/components/molecules/SectionHeader.svelte +1 -1
- package/dist/components/molecules/SeekSlider.svelte +6 -6
- package/dist/components/molecules/SegmentedControl.svelte +7 -7
- package/dist/components/molecules/SortableList.svelte +2 -2
- package/dist/components/molecules/SplitButton.svelte +1 -1
- package/dist/components/molecules/Tabs.svelte +160 -7
- package/dist/components/molecules/ThemePicker.svelte +1 -1
- package/dist/components/molecules/Timestamp.svelte +1 -1
- package/dist/components/molecules/Toggle.svelte +5 -3
- package/dist/components/molecules/Tooltip.svelte +2 -2
- package/dist/components/molecules/VolumeSlider.svelte +3 -3
- package/dist/components/organisms/ConversationFeed.svelte +1 -1
- package/dist/components/organisms/ConversationPanel.svelte +3 -3
- package/dist/components/organisms/DataTable.svelte +18 -16
- package/dist/fx.d.ts +149 -0
- package/dist/fx.js +111 -0
- package/dist/fx.md +58 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +8 -2
- package/dist/looks/README.md +101 -0
- package/dist/looks/depth.css +939 -0
- package/dist/looks/editorial.css +545 -0
- package/dist/looks/tactile.css +871 -0
- package/dist/pointer.d.ts +3 -0
- package/dist/pointer.js +40 -0
- package/dist/stores/fx.svelte.d.ts +17 -0
- package/dist/stores/fx.svelte.js +64 -0
- package/dist/stores/theme.svelte.d.ts +8 -2
- package/dist/stores/theme.svelte.js +33 -7
- package/dist/styles/themes.css +249 -249
- package/dist/styles/tokens.css +37 -2
- package/dist/theme-mode.d.ts +31 -0
- package/dist/theme-mode.js +19 -0
- package/package.json +3 -2
package/dist/fx.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// `data-fx-<family>="<variant>"` on <html> or any ancestor picks a family's
|
|
2
|
+
// effect; without the attribute the first variant applies.
|
|
3
|
+
export const FX = {
|
|
4
|
+
hover: {
|
|
5
|
+
label: 'Button hover',
|
|
6
|
+
variants: [
|
|
7
|
+
{ id: 'smoke', label: 'Smoke' },
|
|
8
|
+
{ id: 'ember', label: 'Ember' },
|
|
9
|
+
{ id: 'aurora', label: 'Aurora' },
|
|
10
|
+
{ id: 'none', label: 'Off' },
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
check: {
|
|
14
|
+
label: 'Checkbox',
|
|
15
|
+
variants: [
|
|
16
|
+
{ id: 'draw', label: 'Draw' },
|
|
17
|
+
{ id: 'pop', label: 'Pop' },
|
|
18
|
+
{ id: 'confetti', label: 'Confetti' },
|
|
19
|
+
{ id: 'morph', label: 'Morph' },
|
|
20
|
+
{ id: 'none', label: 'Off' },
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
radio: {
|
|
24
|
+
label: 'Radio',
|
|
25
|
+
variants: [
|
|
26
|
+
{ id: 'spring', label: 'Spring' },
|
|
27
|
+
{ id: 'liquid', label: 'Liquid' },
|
|
28
|
+
{ id: 'none', label: 'Off' },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
switch: {
|
|
32
|
+
label: 'Switch',
|
|
33
|
+
variants: [
|
|
34
|
+
{ id: 'squash', label: 'Squash' },
|
|
35
|
+
{ id: 'glow', label: 'Glow' },
|
|
36
|
+
{ id: 'calm', label: 'Calm' },
|
|
37
|
+
{ id: 'none', label: 'Off' },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
focus: {
|
|
41
|
+
label: 'Input focus',
|
|
42
|
+
variants: [
|
|
43
|
+
{ id: 'bloom', label: 'Bloom' },
|
|
44
|
+
{ id: 'label', label: 'Spring label' },
|
|
45
|
+
{ id: 'none', label: 'Off' },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
tabs: {
|
|
49
|
+
label: 'Tabs',
|
|
50
|
+
variants: [
|
|
51
|
+
{ id: 'pill', label: 'Pill' },
|
|
52
|
+
{ id: 'stretch', label: 'Stretch' },
|
|
53
|
+
{ id: 'ink', label: 'Ink' },
|
|
54
|
+
{ id: 'none', label: 'Off' },
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
copy: {
|
|
58
|
+
label: 'Copy',
|
|
59
|
+
variants: [
|
|
60
|
+
{ id: 'burst', label: 'Burst' },
|
|
61
|
+
{ id: 'tick', label: 'Tick' },
|
|
62
|
+
{ id: 'none', label: 'Off' },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
link: {
|
|
66
|
+
label: 'Link hover',
|
|
67
|
+
variants: [
|
|
68
|
+
{ id: 'sweep', label: 'Sweep' },
|
|
69
|
+
{ id: 'highlight', label: 'Highlight' },
|
|
70
|
+
{ id: 'none', label: 'Off' },
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
card: {
|
|
74
|
+
label: 'Card hover',
|
|
75
|
+
variants: [
|
|
76
|
+
{ id: 'spotlight', label: 'Spotlight' },
|
|
77
|
+
{ id: 'orbit', label: 'Orbit' },
|
|
78
|
+
{ id: 'none', label: 'Off' },
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
export const FX_FAMILIES = Object.keys(FX);
|
|
83
|
+
export const DEFAULT_FX = Object.fromEntries(FX_FAMILIES.map((f) => [f, FX[f].variants[0].id]));
|
|
84
|
+
export function fxAttr(family) {
|
|
85
|
+
return `data-fx-${family}`;
|
|
86
|
+
}
|
|
87
|
+
export function isFxVariant(family, v) {
|
|
88
|
+
return FX[family].variants.some((x) => x.id === v);
|
|
89
|
+
}
|
|
90
|
+
/** Settings from an untrusted blob: unknown families are dropped, unknown
|
|
91
|
+
* variants fall back to `defaults`. */
|
|
92
|
+
export function parseFx(raw, defaults = DEFAULT_FX) {
|
|
93
|
+
const blob = raw && typeof raw === 'object' ? raw : {};
|
|
94
|
+
const out = { ...defaults };
|
|
95
|
+
for (const f of FX_FAMILIES)
|
|
96
|
+
if (isFxVariant(f, blob[f]))
|
|
97
|
+
out[f] = blob[f];
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
export function fxDiff(settings) {
|
|
101
|
+
const out = {};
|
|
102
|
+
for (const f of FX_FAMILIES)
|
|
103
|
+
if (settings[f] !== DEFAULT_FX[f])
|
|
104
|
+
out[f] = settings[f];
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
/** Inline <head> script painting the persisted `data-fx-*` before first paint. */
|
|
108
|
+
export function fxPrepaint(key) {
|
|
109
|
+
const valid = Object.fromEntries(FX_FAMILIES.map((f) => [f, FX[f].variants.slice(1).map((v) => v.id)]));
|
|
110
|
+
return `(function(){try{var s=localStorage.getItem(${JSON.stringify(key)});if(!s)return;var b=JSON.parse(s),v=${JSON.stringify(valid)},d=document.documentElement;if(!b||typeof b!=='object')return;for(var f in v)if(v[f].indexOf(b[f])>=0)d.setAttribute('data-fx-'+f,b[f])}catch(e){}})();`;
|
|
111
|
+
}
|
package/dist/fx.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Micro-interactions
|
|
2
|
+
|
|
3
|
+
Each component family has a set of effects. One attribute on `<html>`, or on any ancestor, picks the
|
|
4
|
+
effect for that family:
|
|
5
|
+
|
|
6
|
+
```html
|
|
7
|
+
<html data-fx-hover="ember" data-fx-check="pop"></html>
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
With no attribute the first variant applies. `none` turns the family's effect off.
|
|
11
|
+
|
|
12
|
+
| Family | Attribute | Variants (first = default) |
|
|
13
|
+
| -------- | ---------------- | --------------------------------------- |
|
|
14
|
+
| `hover` | `data-fx-hover` | `smoke` `ember` `aurora` `none` |
|
|
15
|
+
| `check` | `data-fx-check` | `draw` `pop` `confetti` `morph` `none` |
|
|
16
|
+
| `radio` | `data-fx-radio` | `spring` `liquid` `none` |
|
|
17
|
+
| `switch` | `data-fx-switch` | `squash` `glow` `calm` `none` |
|
|
18
|
+
| `focus` | `data-fx-focus` | `bloom` `label` `none` |
|
|
19
|
+
| `tabs` | `data-fx-tabs` | `pill` `stretch` `ink` `none` |
|
|
20
|
+
| `copy` | `data-fx-copy` | `burst` `tick` `none` |
|
|
21
|
+
| `link` | `data-fx-link` | `sweep` `highlight` `none` |
|
|
22
|
+
| `card` | `data-fx-card` | `spotlight` `orbit` `none` |
|
|
23
|
+
|
|
24
|
+
Loaders aren't a page setting. They're component props: Spinner, Progress, Skeleton and Button
|
|
25
|
+
`loadingStyle`.
|
|
26
|
+
|
|
27
|
+
All effects take their colours from the theme, fall back to a static state change under
|
|
28
|
+
`prefers-reduced-motion: reduce`, only animate hover on fine pointers, and never shift layout.
|
|
29
|
+
|
|
30
|
+
## Persisted settings
|
|
31
|
+
|
|
32
|
+
```svelte
|
|
33
|
+
<script>
|
|
34
|
+
import { FX_PREPAINT, MotionSettings, fx } from '@dorsk/tsumikit';
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<svelte:head>{@html `<script>${FX_PREPAINT}</` + `script>`}</svelte:head>
|
|
38
|
+
|
|
39
|
+
<MotionSettings size="sm" />
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- `fx` holds the settings (`fx.settings`, `fx.get(family)`, `fx.set(family, variant)`, `fx.reset()`), stores
|
|
43
|
+
the non-default families in localStorage under `tsumikit-fx` and writes `data-fx-*` on `<html>`. Defaults
|
|
44
|
+
leave the attribute absent.
|
|
45
|
+
- `FX_PREPAINT` paints the saved attributes before first paint. Inline it in `<head>` next to
|
|
46
|
+
`THEME_PREPAINT`.
|
|
47
|
+
- `fx.onchange` and `fx.hydrate(settings)` let an app keep the settings in its own (server-side) store.
|
|
48
|
+
- `FX`, `FX_FAMILIES`, `DEFAULT_FX` and `parseFx` expose the registry for custom controls.
|
|
49
|
+
|
|
50
|
+
## Writing an effect
|
|
51
|
+
|
|
52
|
+
Scope every variant with `:where()` so the selector keeps zero specificity. Give the first variant the
|
|
53
|
+
`:not([…])` default:
|
|
54
|
+
|
|
55
|
+
```css
|
|
56
|
+
:where(:root:not([data-fx-hover]), [data-fx-hover='smoke']) .btn-primary::before { … }
|
|
57
|
+
:where([data-fx-hover='ember']) .btn-primary::before { … }
|
|
58
|
+
```
|
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export { default as Swatch } from './components/atoms/Swatch.svelte';
|
|
|
31
31
|
export { default as Switch } from './components/atoms/Switch.svelte';
|
|
32
32
|
export { default as Text } from './components/atoms/Text.svelte';
|
|
33
33
|
export { default as Textarea } from './components/atoms/Textarea.svelte';
|
|
34
|
+
export { default as Thinking } from './components/atoms/Thinking.svelte';
|
|
34
35
|
export { default as TimeDisplay } from './components/atoms/TimeDisplay.svelte';
|
|
35
36
|
export { default as AppShell } from './components/layouts/AppShell.svelte';
|
|
36
37
|
export { default as AutoGrid } from './components/layouts/AutoGrid.svelte';
|
|
@@ -74,10 +75,12 @@ export { default as IconButton } from './components/molecules/IconButton.svelte'
|
|
|
74
75
|
export { default as InputGroup } from './components/molecules/InputGroup.svelte';
|
|
75
76
|
export { default as KeyValue, type KeyValueRow, type KeyValueTone, } from './components/molecules/KeyValue.svelte';
|
|
76
77
|
export { default as LoadMore } from './components/molecules/LoadMore.svelte';
|
|
78
|
+
export { default as LookPicker } from './components/molecules/LookPicker.svelte';
|
|
77
79
|
export { default as Menu, type MenuItem } from './components/molecules/Menu.svelte';
|
|
78
80
|
export { default as Meter } from './components/molecules/Meter.svelte';
|
|
79
81
|
export { default as Metric, default as StatTile, type MetricSegment, } from './components/molecules/Metric.svelte';
|
|
80
82
|
export { default as Modal } from './components/molecules/Modal.svelte';
|
|
83
|
+
export { default as MotionSettings } from './components/molecules/MotionSettings.svelte';
|
|
81
84
|
export { default as OptionButton } from './components/molecules/OptionButton.svelte';
|
|
82
85
|
export { default as Pagination } from './components/molecules/Pagination.svelte';
|
|
83
86
|
export { default as Popover } from './components/molecules/Popover.svelte';
|
|
@@ -111,18 +114,21 @@ export { default as FilterSearchBar } from './components/organisms/FilterSearchB
|
|
|
111
114
|
export { formatCount, hasCount } from './count';
|
|
112
115
|
export { EMOJI_GROUPS, type EmojiEntry, type EmojiGroup, searchEmoji } from './emoji';
|
|
113
116
|
export { FIELD_KEY, type FieldContext, getFieldContext, setFieldContext, warnUnlabelled, } from './field-context';
|
|
117
|
+
export { DEFAULT_FX, FX, FX_FAMILIES, type FxFamily, type FxSettings, type FxVariant, fxAttr, fxDiff, fxPrepaint, isFxVariant, parseFx, } from './fx';
|
|
114
118
|
export { escapeHtml, type Highlight, type RenderMarkdownOptions, renderMarkdown, stripAnsi, } from './markdown';
|
|
115
119
|
export { describeMediaTime, formatMediaTime, isLiveDuration } from './media-time';
|
|
120
|
+
export { trackPointer } from './pointer';
|
|
116
121
|
export * as filterQuery from './query';
|
|
117
122
|
export { type AndNode, activeToken, compilePredicate, defaultOperator, type ExprNode, type FieldDef, type FieldType, type FilterNode, filters, findField, freeText, type LeafNode, type NotNode, OPERATORS, type Operator, type OperatorId, type OrNode, operatorByCode, operatorById, operatorsFor, parse, type Query, type QueryNode, resolveValues, type Schema, type Suggestion, type SuggestKind, type SuggestState, serialize, serializeFilter, singleQuery, suggest, suggestSingle, type TextNode, toSql, type ValueContext, type ValueOption, type ValueProvider, walk, } from './query';
|
|
118
123
|
export { type OptionSection, sectionOptions } from './select-options';
|
|
119
124
|
export type { ControlSize } from './size';
|
|
120
125
|
export { playSound, type SoundMuted, type SoundName } from './sound';
|
|
121
126
|
export { fontScale, SCALE_LEVELS, type ScaleLevel } from './stores/fontscale.svelte';
|
|
127
|
+
export { FX_PREPAINT, fx } from './stores/fx.svelte';
|
|
122
128
|
export { sound } from './stores/sound.svelte';
|
|
123
|
-
export { type Mode, THEMES, theme } from './stores/theme.svelte';
|
|
129
|
+
export { type Mode, THEME_PREPAINT, THEMES, theme } from './stores/theme.svelte';
|
|
124
130
|
export { type Toast, type ToastAction, type ToastOptions, type ToastTone, type ToastToneInput, toasts, } from './stores/toast.svelte';
|
|
125
|
-
export { AUTO_THEME, chooseTheme, DEFAULT_THEME_PREFERENCE, pickerValue, preferenceFrom, resolveTheme, type SlotOf, type ThemeChoice, type ThemePreference, type ThemeSlot, } from './theme-mode';
|
|
131
|
+
export { AUTO_THEME, chooseTheme, DEFAULT_LOOK, DEFAULT_THEME_PREFERENCE, LOOKS, type LookId, type LookStatus, parseLook, pickerValue, preferenceFrom, resolveTheme, type SlotOf, type ThemeChoice, type ThemePreference, type ThemeSlot, themePrepaint, } from './theme-mode';
|
|
126
132
|
export { formatTimestamp, localTimeZone, relativeTime, type TimeInput, type TimestampMode, } from './timestamp';
|
|
127
133
|
export { canonicalTone, type Tone } from './tone';
|
|
128
134
|
export { pathCandidates, type TruncateMode, type TruncateOptions, truncate } from './truncate';
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ export { default as Switch } from './components/atoms/Switch.svelte';
|
|
|
34
34
|
// ---- atoms ----
|
|
35
35
|
export { default as Text } from './components/atoms/Text.svelte';
|
|
36
36
|
export { default as Textarea } from './components/atoms/Textarea.svelte';
|
|
37
|
+
export { default as Thinking } from './components/atoms/Thinking.svelte';
|
|
37
38
|
export { default as TimeDisplay } from './components/atoms/TimeDisplay.svelte';
|
|
38
39
|
// ---- layouts ----
|
|
39
40
|
export { default as AppShell } from './components/layouts/AppShell.svelte';
|
|
@@ -79,12 +80,14 @@ export { default as IconButton } from './components/molecules/IconButton.svelte'
|
|
|
79
80
|
export { default as InputGroup } from './components/molecules/InputGroup.svelte';
|
|
80
81
|
export { default as KeyValue, } from './components/molecules/KeyValue.svelte';
|
|
81
82
|
export { default as LoadMore } from './components/molecules/LoadMore.svelte';
|
|
83
|
+
export { default as LookPicker } from './components/molecules/LookPicker.svelte';
|
|
82
84
|
export { default as Menu } from './components/molecules/Menu.svelte';
|
|
83
85
|
export { default as Meter } from './components/molecules/Meter.svelte';
|
|
84
86
|
export { default as Metric,
|
|
85
87
|
// StatTile is an alias for Metric — same component, dashboard-friendly name.
|
|
86
88
|
default as StatTile, } from './components/molecules/Metric.svelte';
|
|
87
89
|
export { default as Modal } from './components/molecules/Modal.svelte';
|
|
90
|
+
export { default as MotionSettings } from './components/molecules/MotionSettings.svelte';
|
|
88
91
|
export { default as OptionButton } from './components/molecules/OptionButton.svelte';
|
|
89
92
|
export { default as Pagination } from './components/molecules/Pagination.svelte';
|
|
90
93
|
export { default as Popover } from './components/molecules/Popover.svelte';
|
|
@@ -119,19 +122,22 @@ export { default as FilterSearchBar } from './components/organisms/FilterSearchB
|
|
|
119
122
|
export { formatCount, hasCount } from './count';
|
|
120
123
|
export { EMOJI_GROUPS, searchEmoji } from './emoji';
|
|
121
124
|
export { FIELD_KEY, getFieldContext, setFieldContext, warnUnlabelled, } from './field-context';
|
|
125
|
+
export { DEFAULT_FX, FX, FX_FAMILIES, fxAttr, fxDiff, fxPrepaint, isFxVariant, parseFx, } from './fx';
|
|
122
126
|
export { escapeHtml, renderMarkdown, stripAnsi, } from './markdown';
|
|
123
127
|
export { describeMediaTime, formatMediaTime, isLiveDuration } from './media-time';
|
|
128
|
+
export { trackPointer } from './pointer';
|
|
124
129
|
// ---- query core (headless: schema / parser / AST / suggest / compilers) ----
|
|
125
130
|
export * as filterQuery from './query';
|
|
126
131
|
export { activeToken, compilePredicate, defaultOperator, filters, findField, freeText, OPERATORS, operatorByCode, operatorById, operatorsFor, parse, resolveValues, serialize, serializeFilter, singleQuery, suggest, suggestSingle, toSql, walk, } from './query';
|
|
127
132
|
export { sectionOptions } from './select-options';
|
|
128
133
|
export { playSound } from './sound';
|
|
129
134
|
export { fontScale, SCALE_LEVELS } from './stores/fontscale.svelte';
|
|
135
|
+
export { FX_PREPAINT, fx } from './stores/fx.svelte';
|
|
130
136
|
// ---- stores / actions ----
|
|
131
137
|
export { sound } from './stores/sound.svelte';
|
|
132
|
-
export { THEMES, theme } from './stores/theme.svelte';
|
|
138
|
+
export { THEME_PREPAINT, THEMES, theme } from './stores/theme.svelte';
|
|
133
139
|
export { toasts, } from './stores/toast.svelte';
|
|
134
|
-
export { AUTO_THEME, chooseTheme, DEFAULT_THEME_PREFERENCE, pickerValue, preferenceFrom, resolveTheme, } from './theme-mode';
|
|
140
|
+
export { AUTO_THEME, chooseTheme, DEFAULT_LOOK, DEFAULT_THEME_PREFERENCE, LOOKS, parseLook, pickerValue, preferenceFrom, resolveTheme, themePrepaint, } from './theme-mode';
|
|
135
141
|
export { formatTimestamp, localTimeZone, relativeTime, } from './timestamp';
|
|
136
142
|
export { canonicalTone } from './tone';
|
|
137
143
|
export { pathCandidates, truncate } from './truncate';
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Looks
|
|
2
|
+
|
|
3
|
+
A look is the kit's structural style: surfaces, edges, elevation, control fills and
|
|
4
|
+
interactions. It is its own axis, independent of the theme (the colors) and of the
|
|
5
|
+
light/dark mode. Any look works with any of the 30 built-in themes and with your own.
|
|
6
|
+
|
|
7
|
+
| id | status | character |
|
|
8
|
+
| ----------- | ------------ | ------------------------------------------------------ |
|
|
9
|
+
| `default` | stable | polished: the kit as shipped, no stylesheet needed |
|
|
10
|
+
| `depth` | stable | layered elevation, inner highlights, accent glow |
|
|
11
|
+
| `tactile` | stable | soft raised and pressed surfaces |
|
|
12
|
+
| `editorial` | experimental | bold type scale, rules, solid tones; may change |
|
|
13
|
+
|
|
14
|
+
## Use
|
|
15
|
+
|
|
16
|
+
Import the looks you offer, next to the kit's stylesheet. A look costs nothing until it is
|
|
17
|
+
active: every rule is scoped to `:root[data-look="<id>"]`.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import '@dorsk/tsumikit/styles/app.css';
|
|
21
|
+
import '@dorsk/tsumikit/looks/depth.css';
|
|
22
|
+
import '@dorsk/tsumikit/looks/tactile.css';
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The theme store owns the look and persists it with the theme preference
|
|
26
|
+
(`localStorage['tsumikit-theme']`, a `look` key that is absent for the default look):
|
|
27
|
+
|
|
28
|
+
```svelte
|
|
29
|
+
<script lang="ts">
|
|
30
|
+
import { LookPicker, ThemePicker, theme } from '@dorsk/tsumikit';
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<LookPicker />
|
|
34
|
+
<ThemePicker auto />
|
|
35
|
+
<p>{theme.look}</p>
|
|
36
|
+
<button onclick={() => theme.setLook('depth')}>Depth</button>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`LOOKS` lists `{ id, label, status }`. `<LookPicker experimental={false} />` hides
|
|
40
|
+
experimental looks; `looks={['default', 'depth']}` restricts and orders them.
|
|
41
|
+
|
|
42
|
+
The store paints `data-theme`, `data-mode="light|dark"` and `data-look` (absent for the
|
|
43
|
+
default look) on `<html>`. Inline `THEME_PREPAINT` in `<head>` to paint all three before
|
|
44
|
+
first paint:
|
|
45
|
+
|
|
46
|
+
```svelte
|
|
47
|
+
<script lang="ts">
|
|
48
|
+
import { THEME_PREPAINT } from '@dorsk/tsumikit';
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<svelte:head>
|
|
52
|
+
{@html `<script>${THEME_PREPAINT}</` + `script>`}
|
|
53
|
+
</svelte:head>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If you register your own themes, build the script with
|
|
57
|
+
`themePrepaint('tsumikit-theme', [...THEMES, ...yours])` so legacy single-id values resolve.
|
|
58
|
+
|
|
59
|
+
Apps that persist settings server-side get `look` inside the preference passed to
|
|
60
|
+
`theme.onchange`, and can replay it with `theme.hydrate(pref)`. A preference without
|
|
61
|
+
`look` keeps the current look.
|
|
62
|
+
|
|
63
|
+
### Pointer effects
|
|
64
|
+
|
|
65
|
+
Some looks light the surface under the mouse. Call `trackPointer()` once, client side; it
|
|
66
|
+
sets `--pointer-x` / `--pointer-y` on the hovered `[data-tsu]` element and its `[data-tsu]`
|
|
67
|
+
ancestors and returns a disposer. Looks degrade gracefully without it.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
$effect(() => trackPointer());
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Writing a look
|
|
74
|
+
|
|
75
|
+
`src/lib/looks/<id>.css`, plus an entry in `LOOKS` (`src/lib/theme-mode.ts`).
|
|
76
|
+
`tests/looks-lint.test.js` enforces the first three rules.
|
|
77
|
+
|
|
78
|
+
1. **Scope everything** under `:root[data-look="<id>"]`. Light-only tweaks go under
|
|
79
|
+
`:root[data-look="<id>"][data-mode="light"]`; prefer recipes that work in both modes.
|
|
80
|
+
`@keyframes` are global: namespace their names.
|
|
81
|
+
2. **No literal colors.** No hex, no `rgb()`/`hsl()`/`oklch()`/`lab()` with channel
|
|
82
|
+
numbers, no named colors (`black` and `white` included) except `transparent` and
|
|
83
|
+
`currentColor`. Derive every color from the active theme:
|
|
84
|
+
`var(--accent)`, `color-mix(in oklab, var(--accent) 30%, transparent)`,
|
|
85
|
+
`oklch(from var(--accent) l c h / 0.4)`; shadows from
|
|
86
|
+
`oklch(from var(--bg) calc(l * 0.4) c h / 0.6)` or
|
|
87
|
+
`color-mix(in oklab, var(--text) 18%, transparent)`.
|
|
88
|
+
3. **No `transition: all`.** Durations come from `--dur-*`.
|
|
89
|
+
4. **Tokens first.** Override the structural tokens in `styles/tokens.css` ("Structural
|
|
90
|
+
look": surface, raised, overlay, control, track, thumb and button fills, edges and
|
|
91
|
+
shadows, `--edge-width`, `--hover-lift`, `--focus-glow`). Add per-component rules
|
|
92
|
+
(`:root[data-look="<id>"] [data-tsu="Card"]`) only where a token cannot express it.
|
|
93
|
+
5. **Motion:** hover-only effects inside `@media (hover: hover) and (pointer: fine)`;
|
|
94
|
+
respect `prefers-reduced-motion`.
|
|
95
|
+
6. **Contrast**, for every theme in both modes: text ≥ 4.5:1, UI boundaries and focus
|
|
96
|
+
rings ≥ 3:1. A look must not lower text contrast; it should rarely touch text colors.
|
|
97
|
+
|
|
98
|
+
Palette tokens available to a look: `--bg`, `--bg-elevated`, `--bg-elevated-2`,
|
|
99
|
+
`--surface`, `--border`, `--border-strong`, `--text`, `--text-muted`, `--text-faint`,
|
|
100
|
+
`--accent`, `--text-on-accent`, `--ok`, `--warn`, `--danger`, `--info` and the `--c-*`
|
|
101
|
+
palette.
|