@motion-proto/live-tokens 0.94.2 → 0.94.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/CHANGELOG.md +11 -0
- package/package.json +1 -1
- package/src/demo/ChromeSelect.svelte +111 -0
- package/src/demo/DemoBar.svelte +10 -9
- package/src/demo/SketchSelect.svelte +36 -12
- package/src/demo/ThemeSelect.svelte +34 -11
- package/src/system/styles/CONVENTIONS.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.94.3 — Demo bar chrome
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **The demo's Theme and Sketch bar stays legible in every theme.** The bar
|
|
8
|
+
painted from the open theme's scrim, and its selects were themed Buttons, so
|
|
9
|
+
a theme or sketchstyle could leave its labels unreadable. The bar now paints
|
|
10
|
+
as editor chrome from the `--ui-*` tokens, like the editor overlay, and
|
|
11
|
+
sketch mode leaves it alone. `ThemeSelect` and `SketchSelect` take a
|
|
12
|
+
`chrome` prop for the bar. Elsewhere they stay themed.
|
|
13
|
+
|
|
3
14
|
## 0.94.2 — Palette step confirm
|
|
4
15
|
|
|
5
16
|
### Fixed
|
package/package.json
CHANGED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import UIMenuButton from '../editor/ui/UIMenuButton.svelte';
|
|
3
|
+
import UIOptionList from '../editor/ui/UIOptionList.svelte';
|
|
4
|
+
import UIOptionItem from '../editor/ui/UIOptionItem.svelte';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
label: string;
|
|
8
|
+
items: { value: string; label: string }[];
|
|
9
|
+
value: string;
|
|
10
|
+
/** Trigger text when `value` matches no item. Defaults to the label. */
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
error?: string;
|
|
14
|
+
onchange: (value: string) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
label: heading,
|
|
19
|
+
items,
|
|
20
|
+
value,
|
|
21
|
+
placeholder = heading,
|
|
22
|
+
disabled = false,
|
|
23
|
+
error = '',
|
|
24
|
+
onchange,
|
|
25
|
+
}: Props = $props();
|
|
26
|
+
|
|
27
|
+
const selectedLabel = $derived(items.find((item) => item.value === value)?.label ?? placeholder);
|
|
28
|
+
|
|
29
|
+
function select(next: string, close: () => void): void {
|
|
30
|
+
close();
|
|
31
|
+
if (next === value) return;
|
|
32
|
+
onchange(next);
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<!-- LabeledSelect's twin in the editor's --ui-* chrome. It needs an
|
|
37
|
+
.editor-page ancestor, which scopes those tokens and hosts the menu. -->
|
|
38
|
+
<div class="chrome-select" inert={disabled || items.length === 0}>
|
|
39
|
+
<span class="chrome-select-label">{heading}</span>
|
|
40
|
+
|
|
41
|
+
<UIMenuButton header={heading} triggerClass="chrome-select-trigger" menuMinWidth="12rem">
|
|
42
|
+
{#snippet trigger()}
|
|
43
|
+
<span>{selectedLabel}</span>
|
|
44
|
+
<i class="fas fa-chevron-down" aria-hidden="true"></i>
|
|
45
|
+
{/snippet}
|
|
46
|
+
{#snippet children({ close })}
|
|
47
|
+
<UIOptionList>
|
|
48
|
+
{#each items as item (item.value)}
|
|
49
|
+
<UIOptionItem active={item.value === value} onclick={() => select(item.value, close)}>
|
|
50
|
+
{#snippet label()}{item.label}{/snippet}
|
|
51
|
+
</UIOptionItem>
|
|
52
|
+
{/each}
|
|
53
|
+
</UIOptionList>
|
|
54
|
+
{/snippet}
|
|
55
|
+
</UIMenuButton>
|
|
56
|
+
|
|
57
|
+
{#if error}
|
|
58
|
+
<span class="error" aria-live="polite">{error}</span>
|
|
59
|
+
{/if}
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<style>
|
|
63
|
+
.chrome-select {
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-wrap: wrap;
|
|
66
|
+
align-items: center;
|
|
67
|
+
gap: var(--ui-space-8);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.chrome-select[inert] {
|
|
71
|
+
opacity: var(--ui-opacity-disabled);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.chrome-select-label {
|
|
75
|
+
font-size: var(--ui-font-size-xs);
|
|
76
|
+
font-weight: var(--ui-font-weight-semibold);
|
|
77
|
+
color: var(--ui-text-secondary);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.chrome-select :global(.chrome-select-trigger) {
|
|
81
|
+
min-width: 9rem;
|
|
82
|
+
justify-content: space-between;
|
|
83
|
+
gap: var(--ui-space-12);
|
|
84
|
+
padding: var(--ui-space-6) var(--ui-space-10);
|
|
85
|
+
font-size: var(--ui-font-size-xs);
|
|
86
|
+
color: var(--ui-text-primary);
|
|
87
|
+
background: var(--ui-surface-high);
|
|
88
|
+
border: 1px solid var(--ui-border);
|
|
89
|
+
border-radius: var(--ui-radius-md);
|
|
90
|
+
transition:
|
|
91
|
+
background var(--ui-transition-fast),
|
|
92
|
+
border-color var(--ui-transition-fast);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.chrome-select :global(.chrome-select-trigger:hover),
|
|
96
|
+
.chrome-select :global(.chrome-select-trigger[aria-expanded='true']) {
|
|
97
|
+
background: var(--ui-surface-highest);
|
|
98
|
+
border-color: var(--ui-border-higher);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.chrome-select :global(.chrome-select-trigger .fa-chevron-down) {
|
|
102
|
+
font-size: 0.625rem;
|
|
103
|
+
color: var(--ui-text-secondary);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.error {
|
|
107
|
+
max-width: 20rem;
|
|
108
|
+
font-size: var(--ui-font-size-xs);
|
|
109
|
+
color: var(--ui-link-broken);
|
|
110
|
+
}
|
|
111
|
+
</style>
|
package/src/demo/DemoBar.svelte
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import '../editor/styles/ui-editor.css';
|
|
2
3
|
import ThemeSelect from './ThemeSelect.svelte';
|
|
3
4
|
import SketchSelect from './SketchSelect.svelte';
|
|
4
5
|
|
|
@@ -11,11 +12,14 @@
|
|
|
11
12
|
} = $props();
|
|
12
13
|
</script>
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
<!-- Editor chrome, like the overlay: the bar paints from --ui-*, so every
|
|
16
|
+
theme leaves it legible, and data-live-tokens-chrome keeps sketch mode off
|
|
17
|
+
it. .editor-page scopes the --ui-* tokens and hosts the selects' menus. -->
|
|
18
|
+
<div class="demo-bar editor-page" data-live-tokens-chrome>
|
|
15
19
|
<div class="bar">
|
|
16
20
|
<div class="pickers">
|
|
17
|
-
<ThemeSelect
|
|
18
|
-
<SketchSelect
|
|
21
|
+
<ThemeSelect chrome onpick={onThemePick} />
|
|
22
|
+
<SketchSelect chrome onpick={onSketchPick} />
|
|
19
23
|
</div>
|
|
20
24
|
</div>
|
|
21
25
|
</div>
|
|
@@ -27,9 +31,9 @@
|
|
|
27
31
|
left: 0;
|
|
28
32
|
right: 0;
|
|
29
33
|
z-index: var(--z-sticky);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
border-bottom:
|
|
34
|
+
--sketch-icon-off: none;
|
|
35
|
+
background: var(--ui-surface-lower);
|
|
36
|
+
border-bottom: 1px solid var(--ui-border-low);
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
/* The bar repeats the demo's .kit grid, so its content lines up with the
|
|
@@ -64,9 +68,6 @@
|
|
|
64
68
|
@media (max-width: 600px) {
|
|
65
69
|
.demo-bar {
|
|
66
70
|
position: static;
|
|
67
|
-
background: none;
|
|
68
|
-
backdrop-filter: none;
|
|
69
|
-
border-bottom: none;
|
|
70
71
|
}
|
|
71
72
|
.bar {
|
|
72
73
|
padding: var(--space-64) var(--space-16) 0;
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import LabeledSelect from './LabeledSelect.svelte';
|
|
3
|
+
import ChromeSelect from './ChromeSelect.svelte';
|
|
3
4
|
import { setSketch, sketchPick, sketchStyles, unsavedSketchStyle } from '../editor/core/sketch';
|
|
4
5
|
|
|
5
|
-
let {
|
|
6
|
+
let {
|
|
7
|
+
inline = false,
|
|
8
|
+
size = 'default',
|
|
9
|
+
chrome = false,
|
|
10
|
+
onpick,
|
|
11
|
+
}: {
|
|
12
|
+
inline?: boolean;
|
|
13
|
+
size?: 'default' | 'small';
|
|
14
|
+
/** Paint in the editor's --ui-* chrome; needs an .editor-page ancestor. */
|
|
15
|
+
chrome?: boolean;
|
|
16
|
+
onpick?: (id: string | null) => void;
|
|
17
|
+
} = $props();
|
|
6
18
|
|
|
7
19
|
// The empty string is the placeholder's value, so off needs a name of its own.
|
|
8
20
|
const OFF = 'none';
|
|
@@ -45,14 +57,26 @@
|
|
|
45
57
|
}
|
|
46
58
|
</script>
|
|
47
59
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
{#if chrome}
|
|
61
|
+
<ChromeSelect
|
|
62
|
+
label="Sketch"
|
|
63
|
+
{items}
|
|
64
|
+
value={selected}
|
|
65
|
+
placeholder="Custom"
|
|
66
|
+
disabled={busy}
|
|
67
|
+
{error}
|
|
68
|
+
onchange={changeSketch}
|
|
69
|
+
/>
|
|
70
|
+
{:else}
|
|
71
|
+
<LabeledSelect
|
|
72
|
+
{inline}
|
|
73
|
+
{size}
|
|
74
|
+
label="Sketch"
|
|
75
|
+
{items}
|
|
76
|
+
value={selected}
|
|
77
|
+
placeholder="Custom"
|
|
78
|
+
disabled={busy}
|
|
79
|
+
{error}
|
|
80
|
+
onchange={changeSketch}
|
|
81
|
+
/>
|
|
82
|
+
{/if}
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount } from 'svelte';
|
|
3
3
|
import LabeledSelect from './LabeledSelect.svelte';
|
|
4
|
+
import ChromeSelect from './ChromeSelect.svelte';
|
|
4
5
|
import { listThemes, applyTheme } from '../editor/core/themes/themeService';
|
|
5
6
|
import { openThemeSlug } from '../editor/core/store/editorConfigStore';
|
|
6
7
|
import { carrySketch } from './sketchCarry';
|
|
7
8
|
|
|
8
|
-
let {
|
|
9
|
+
let {
|
|
10
|
+
inline = false,
|
|
11
|
+
size = 'default',
|
|
12
|
+
chrome = false,
|
|
13
|
+
onpick,
|
|
14
|
+
}: {
|
|
15
|
+
inline?: boolean;
|
|
16
|
+
size?: 'default' | 'small';
|
|
17
|
+
/** Paint in the editor's --ui-* chrome; needs an .editor-page ancestor. */
|
|
18
|
+
chrome?: boolean;
|
|
19
|
+
onpick?: (fileName: string) => void;
|
|
20
|
+
} = $props();
|
|
9
21
|
|
|
10
22
|
let themes = $state<{ value: string; label: string }[]>([]);
|
|
11
23
|
let busy = $state(false);
|
|
@@ -40,13 +52,24 @@
|
|
|
40
52
|
|
|
41
53
|
<!-- The open theme drives the selection, so applies from the Themes panel or
|
|
42
54
|
another tab show up here too. -->
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
{#if chrome}
|
|
56
|
+
<ChromeSelect
|
|
57
|
+
label="Theme"
|
|
58
|
+
items={themes}
|
|
59
|
+
value={$openThemeSlug}
|
|
60
|
+
disabled={busy}
|
|
61
|
+
{error}
|
|
62
|
+
onchange={changeTheme}
|
|
63
|
+
/>
|
|
64
|
+
{:else}
|
|
65
|
+
<LabeledSelect
|
|
66
|
+
{inline}
|
|
67
|
+
{size}
|
|
68
|
+
label="Theme"
|
|
69
|
+
items={themes}
|
|
70
|
+
value={$openThemeSlug}
|
|
71
|
+
disabled={busy}
|
|
72
|
+
{error}
|
|
73
|
+
onchange={changeTheme}
|
|
74
|
+
/>
|
|
75
|
+
{/if}
|
|
@@ -9,7 +9,7 @@ Each file plays a distinct role. Don't merge them without understanding why they
|
|
|
9
9
|
| File | Scope | Tokens used | Loaded by | Notes |
|
|
10
10
|
|---|---|---|---|---|
|
|
11
11
|
| `tokens.css` | Themed pages | Defines `--color-*`, `--surface-*`, `--text-*`, `--space-*`, … | `main.ts` | **Runtime-edited.** The token editor rewrites this file via the `themeFileApi` Vite plugin. Starter content; consumers replace at will. |
|
|
12
|
-
| `ui-editor.css` | Editor chrome only | Defines `--ui-*` (opaque grayscale, system fonts) | JS-imported by `Editor.svelte` and `
|
|
12
|
+
| `ui-editor.css` | Editor chrome only | Defines `--ui-*` (opaque grayscale, system fonts) | JS-imported by `Editor.svelte`, `ComponentEditorPage.svelte`, `ColorsPage.svelte`, and the demo's `DemoBar.svelte` | Deliberately isolated from the theme system so editor surfaces stay neutral while live theme edits flow through the components being edited. Never load globally. |
|
|
13
13
|
| `ui-form-controls.css` | Editor chrome only | Consumes `--ui-*` only | JS-imported by `Editor.svelte` and `ComponentEditorPage.svelte` | `.ui-form-select` / `.ui-form-input` / `.ui-form-field-*` classes. Used by `ProjectFontsSection`, `FontStackEditor`, `DialogEditor`, `NotificationEditor`. Theme-immune — must not reference `--font-*`, `--surface-*`, etc. |
|
|
14
14
|
| `site.css` | Themed pages | Consumes theme tokens | Page-imported by `Home.svelte` | Consumer-facing starter typography for the landing page (unscoped `h1`, `p`, `a`, …). Never load on editor pages. Replaceable starter content — users edit or replace this file to style their own site. Components that slot consumer content (Card body, CollapsibleSection content, SectionKit) defend their body typography against these global rules with `@include slot-prose` (from `styles/_slot-prose.scss`): the mixin pins only the owned type axes (font-family, font-size, font-weight, line-height, color) plus list spacing onto slotted `p`/`ul`/`ol`/`li`, while `a`, `strong`, font-style, letter-spacing and text-transform are left to inherit the page (links stay links, a consumer's italics survive). `prose={false}` opts a slot out entirely, and the `check:slot-prose` guard fails the build on a hand-rolled copy of the pin. The demo page no longer imports it; the demo uses scoped classes and component-owned slot typography instead. |
|
|
15
15
|
| `fonts.css` + `fonts/` | Themed pages only | n/a (`@font-face` only) | `main.ts` | Build-special-cased: copied directly to `dist/` without processing so Vite doesn't inline woff2 files as base64. Starter content; editor's font invariant means these never affect chrome. |
|