@motion-proto/live-tokens 0.7.1 → 0.9.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/.claude/skills/live-tokens-add-component/SKILL.md +488 -0
- package/README.md +34 -0
- package/dist-plugin/index.cjs +707 -90
- package/dist-plugin/index.d.cts +1 -0
- package/dist-plugin/index.d.ts +1 -0
- package/dist-plugin/index.js +707 -90
- package/package.json +6 -2
- package/src/app/site.css +1 -1
- package/src/editor/component-editor/CollapsibleSectionEditor.svelte +34 -27
- package/src/editor/component-editor/DialogEditor.svelte +4 -4
- package/src/editor/component-editor/NotificationEditor.svelte +3 -1
- package/src/editor/component-editor/SectionDividerEditor.svelte +439 -112
- package/src/editor/component-editor/StandardButtonsEditor.svelte +13 -1
- package/src/editor/component-editor/editors.d.ts +10 -0
- package/src/editor/component-editor/index.ts +16 -1
- package/src/editor/component-editor/registry.ts +103 -26
- package/src/editor/component-editor/scaffolding/AngleDial.svelte +52 -13
- package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +10 -11
- package/src/editor/component-editor/scaffolding/ComponentsTab.svelte +2 -2
- package/src/editor/component-editor/scaffolding/LinkedBlock.svelte +0 -1
- package/src/editor/component-editor/scaffolding/RadialShapePad.svelte +483 -0
- package/src/editor/component-editor/scaffolding/ShadowBackdrop.svelte +15 -2
- package/src/editor/component-editor/scaffolding/StateBlock.svelte +103 -15
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +9 -6
- package/src/editor/component-editor/scaffolding/TypeEditor.svelte +13 -1
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +239 -25
- package/src/editor/component-editor/scaffolding/buildTypeGroupTokens.ts +1 -0
- package/src/editor/component-editor/scaffolding/componentSources.ts +3 -3
- package/src/editor/component-editor/scaffolding/defaultSections.ts +15 -10
- package/src/editor/component-editor/scaffolding/types.ts +11 -0
- package/src/editor/core/components/componentConfigKeys.ts +22 -3
- package/src/editor/core/components/componentConfigService.ts +2 -2
- package/src/editor/core/components/componentPersist.ts +7 -5
- package/src/editor/core/manifests/manifestService.ts +58 -3
- package/src/editor/core/palettes/familySwap.ts +99 -0
- package/src/editor/core/palettes/paletteDerivation.ts +69 -0
- package/src/editor/core/palettes/tokenRegistry.ts +4 -1
- package/src/editor/core/store/editorStore.ts +206 -12
- package/src/editor/core/store/editorTypes.ts +55 -12
- package/src/editor/core/store/gradientSource.ts +192 -0
- package/src/editor/core/themes/migrations/2026-05-19-collapsiblesection-drop-frame-surface.ts +28 -0
- package/src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts +35 -0
- package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +82 -0
- package/src/editor/core/themes/migrations/2026-05-21-sectiondivider-spacing-to-padding.ts +24 -0
- package/src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts +81 -0
- package/src/editor/core/themes/migrations/index.ts +10 -0
- package/src/editor/core/themes/slices/components.ts +27 -4
- package/src/editor/core/themes/slices/gradients.ts +88 -13
- package/src/editor/core/themes/themeInit.ts +2 -2
- package/src/editor/core/themes/themeTypes.ts +56 -1
- package/src/editor/index.ts +10 -1
- package/src/editor/overlay/ColumnsOverlay.svelte +0 -1
- package/src/editor/overlay/LiveEditorOverlay.svelte +1 -4
- package/src/editor/pages/ComponentEditorPage.svelte +53 -3
- package/src/editor/pages/EditorShell.svelte +53 -3
- package/src/editor/styles/ui-editor.css +1 -0
- package/src/editor/styles/ui-form-controls.css +19 -20
- package/src/editor/ui/BezierCurveEditor.svelte +114 -63
- package/src/editor/ui/EditorViewSwitcher.svelte +0 -1
- package/src/editor/ui/FileLoadList.svelte +22 -5
- package/src/editor/ui/FontStackEditor.svelte +214 -76
- package/src/editor/ui/GradientEditor.svelte +435 -215
- package/src/editor/ui/GradientStopPicker.svelte +11 -3
- package/src/editor/ui/ManifestFileManager.svelte +71 -4
- package/src/editor/ui/PaletteEditor.svelte +52 -79
- package/src/editor/ui/ProjectFontsSection.svelte +328 -293
- package/src/editor/ui/ThemeFileManager.svelte +0 -4
- package/src/editor/ui/UIFontFamilySelector.svelte +0 -1
- package/src/editor/ui/UIFontSizeSelector.svelte +3 -0
- package/src/editor/ui/UIInfoPopover.svelte +0 -1
- package/src/editor/ui/UILetterSpacingSelector.svelte +65 -0
- package/src/editor/ui/UIPaletteSelector.svelte +31 -4
- package/src/editor/ui/UIPillButton.svelte +33 -3
- package/src/editor/ui/UISegmentedControl.svelte +114 -0
- package/src/editor/ui/UITokenSelector.svelte +4 -1
- package/src/editor/ui/VariablesTab.svelte +41 -35
- package/src/editor/ui/palette/OverridesPanel.svelte +14 -37
- package/src/editor/ui/palette/PaletteBase.svelte +3 -3
- package/src/editor/ui/sections/ColumnsSection.svelte +1 -2
- package/src/editor/ui/sections/GradientsSection.svelte +1 -1
- package/src/editor/ui/sections/OverlaysSection.svelte +1 -1
- package/src/editor/ui/sections/ShadowsSection.svelte +1 -1
- package/src/system/components/Button.svelte +2 -2
- package/src/system/components/Card.svelte +29 -1
- package/src/system/components/CollapsibleSection.svelte +25 -2
- package/src/system/components/Dialog.svelte +24 -4
- package/src/system/components/FloatingTokenTags.css +43 -24
- package/src/system/components/FloatingTokenTags.svelte +88 -137
- package/src/system/components/Notification.svelte +8 -1
- package/src/system/components/SectionDivider.svelte +532 -381
- package/src/system/styles/CONVENTIONS.md +1 -1
- package/src/system/styles/fonts.css +3 -16
- package/src/system/styles/tokens.css +356 -1199
- package/src/system/styles/tokens.generated.css +544 -0
- package/src/editor/component-editor/scaffolding/DividerEditor.svelte +0 -94
- package/src/editor/component-editor/scaffolding/GradientCard.svelte +0 -296
|
@@ -457,7 +457,6 @@
|
|
|
457
457
|
font-size: var(--ui-font-size-xs);
|
|
458
458
|
color: var(--ui-text-secondary);
|
|
459
459
|
text-transform: uppercase;
|
|
460
|
-
letter-spacing: 0.05em;
|
|
461
460
|
}
|
|
462
461
|
|
|
463
462
|
/* Two-card pipeline (Editor → Production) — theme card + production card
|
|
@@ -506,7 +505,6 @@
|
|
|
506
505
|
font-size: var(--ui-font-size-xs);
|
|
507
506
|
font-weight: var(--ui-font-weight-semibold);
|
|
508
507
|
text-transform: uppercase;
|
|
509
|
-
letter-spacing: 0.08em;
|
|
510
508
|
color: var(--ui-text-secondary);
|
|
511
509
|
line-height: 1.1;
|
|
512
510
|
}
|
|
@@ -516,7 +514,6 @@
|
|
|
516
514
|
align-items: center;
|
|
517
515
|
gap: var(--ui-space-4);
|
|
518
516
|
font-size: 0.7rem;
|
|
519
|
-
letter-spacing: 0.02em;
|
|
520
517
|
color: var(--ui-text-muted);
|
|
521
518
|
line-height: 1;
|
|
522
519
|
}
|
|
@@ -607,7 +604,6 @@
|
|
|
607
604
|
|
|
608
605
|
.tfm-revert-label {
|
|
609
606
|
font-weight: var(--ui-font-weight-semibold, 600);
|
|
610
|
-
letter-spacing: 0.02em;
|
|
611
607
|
white-space: nowrap;
|
|
612
608
|
}
|
|
613
609
|
|
|
@@ -104,6 +104,9 @@
|
|
|
104
104
|
dropdownGridColumns="auto auto auto auto"
|
|
105
105
|
{onchange}
|
|
106
106
|
>
|
|
107
|
+
{#snippet triggerMeta({ currentValue, activeOption })}
|
|
108
|
+
{activeOption ? (pxByKey[activeOption.key] ?? '—') : (currentValue || '—')}
|
|
109
|
+
{/snippet}
|
|
107
110
|
{#snippet option({ opt, active, select })}
|
|
108
111
|
|
|
109
112
|
<button class="font-size-row" class:active onclick={select}>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import UIVariantSelector from './UIVariantSelector.svelte';
|
|
3
|
+
import UIOptionItem from './UIOptionItem.svelte';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
variable: string;
|
|
7
|
+
component?: string | undefined;
|
|
8
|
+
canBeLinked?: boolean;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
selectionsLocked?: boolean;
|
|
11
|
+
onchange?: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
variable,
|
|
16
|
+
component = undefined,
|
|
17
|
+
canBeLinked = false,
|
|
18
|
+
disabled = false,
|
|
19
|
+
selectionsLocked = false,
|
|
20
|
+
onchange,
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
|
|
23
|
+
const options = [
|
|
24
|
+
{ key: 'tighter', label: 'Tighter', value: '-0.04em' },
|
|
25
|
+
{ key: 'tight', label: 'Tight', value: '-0.02em' },
|
|
26
|
+
{ key: 'normal', label: 'Normal', value: '0' },
|
|
27
|
+
{ key: 'wide', label: 'Wide', value: '0.04em' },
|
|
28
|
+
{ key: 'wider', label: 'Wider', value: '0.08em' },
|
|
29
|
+
] as const;
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<UIVariantSelector
|
|
33
|
+
{variable}
|
|
34
|
+
{component}
|
|
35
|
+
{canBeLinked}
|
|
36
|
+
{disabled}
|
|
37
|
+
{selectionsLocked}
|
|
38
|
+
varPrefix="--letter-spacing-"
|
|
39
|
+
{options}
|
|
40
|
+
{onchange}
|
|
41
|
+
>
|
|
42
|
+
{#snippet option({ opt, active, select })}
|
|
43
|
+
<UIOptionItem {active} onclick={select}>
|
|
44
|
+
{#snippet preview()}
|
|
45
|
+
<span class="ls-sample" style="letter-spacing: var(--letter-spacing-{opt.key});">AV</span>
|
|
46
|
+
{/snippet}
|
|
47
|
+
{#snippet label()}
|
|
48
|
+
{opt.label}
|
|
49
|
+
{/snippet}
|
|
50
|
+
{#snippet meta()}
|
|
51
|
+
{opt.value}
|
|
52
|
+
{/snippet}
|
|
53
|
+
</UIOptionItem>
|
|
54
|
+
{/snippet}
|
|
55
|
+
</UIVariantSelector>
|
|
56
|
+
|
|
57
|
+
<style>
|
|
58
|
+
.ls-sample {
|
|
59
|
+
display: inline-block;
|
|
60
|
+
width: 1.75rem;
|
|
61
|
+
text-align: center;
|
|
62
|
+
font-size: var(--ui-font-size-md);
|
|
63
|
+
color: var(--ui-text-primary);
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
@@ -36,6 +36,11 @@
|
|
|
36
36
|
canBeLinked?: boolean;
|
|
37
37
|
disabled?: boolean;
|
|
38
38
|
selectionsLocked?: boolean;
|
|
39
|
+
/** When set, restrict picker family choices to this family (one of the
|
|
40
|
+
* entries in the `families` list). Other families render but are
|
|
41
|
+
* rendered disabled and not clickable. Out-of-family already-set
|
|
42
|
+
* choices still surface in the trigger meta. */
|
|
43
|
+
familyFilter?: string | null;
|
|
39
44
|
onchange?: () => void;
|
|
40
45
|
}
|
|
41
46
|
|
|
@@ -45,6 +50,7 @@
|
|
|
45
50
|
canBeLinked = false,
|
|
46
51
|
disabled = false,
|
|
47
52
|
selectionsLocked = false,
|
|
53
|
+
familyFilter = null,
|
|
48
54
|
onchange,
|
|
49
55
|
}: Props = $props();
|
|
50
56
|
|
|
@@ -97,7 +103,7 @@
|
|
|
97
103
|
const borderStepKeys = borderSteps.map(s => s.key);
|
|
98
104
|
const textStepKeys = textSteps.map(s => s.key);
|
|
99
105
|
|
|
100
|
-
const familiesWithText = ['neutral', 'canvas', 'brand', 'accent', 'special', 'success', 'warning', 'info', 'danger'];
|
|
106
|
+
const familiesWithText = ['neutral', 'alternate', 'canvas', 'brand', 'accent', 'special', 'success', 'warning', 'info', 'danger'];
|
|
101
107
|
|
|
102
108
|
const allCategories: { id: Category; label: string }[] = [
|
|
103
109
|
{ id: 'palette', label: 'Palette' },
|
|
@@ -405,6 +411,7 @@
|
|
|
405
411
|
}
|
|
406
412
|
|
|
407
413
|
function selectFamily(name: string) {
|
|
414
|
+
if (familyFilter && name !== familyFilter) return;
|
|
408
415
|
selectedFamily = name;
|
|
409
416
|
if (name === chosenFamily && chosenCategory) {
|
|
410
417
|
selectedTab = chosenCategory;
|
|
@@ -543,14 +550,23 @@
|
|
|
543
550
|
<span class="family-label">None</span>
|
|
544
551
|
</button>
|
|
545
552
|
{#each families as fam}
|
|
546
|
-
|
|
553
|
+
{@const outOfFamily = familyFilter !== null && fam.name !== familyFilter}
|
|
554
|
+
<button
|
|
555
|
+
class="family-item"
|
|
556
|
+
class:active={!chosenNone && chosenFamily === fam.name}
|
|
557
|
+
class:out-of-family={outOfFamily}
|
|
558
|
+
disabled={outOfFamily}
|
|
559
|
+
onclick={() => selectFamily(fam.name)}
|
|
560
|
+
>
|
|
547
561
|
<div class="family-swatches">
|
|
548
562
|
<div class="mini-swatch" style="background: var(--color-{fam.name}-300);"></div>
|
|
549
563
|
<div class="mini-swatch" style="background: var(--color-{fam.name}-500);"></div>
|
|
550
564
|
<div class="mini-swatch" style="background: var(--color-{fam.name}-700);"></div>
|
|
551
565
|
</div>
|
|
552
566
|
<span class="family-label">{fam.label}</span>
|
|
553
|
-
|
|
567
|
+
{#if !outOfFamily}
|
|
568
|
+
<i class="fas fa-chevron-right family-arrow"></i>
|
|
569
|
+
{/if}
|
|
554
570
|
</button>
|
|
555
571
|
{/each}
|
|
556
572
|
{#if gradientsAllowed && gradientTokens.length > 0}
|
|
@@ -862,6 +878,18 @@
|
|
|
862
878
|
box-shadow: inset 3px 0 0 var(--ui-text-accent);
|
|
863
879
|
}
|
|
864
880
|
|
|
881
|
+
/* Family is filtered out by `familyFilter`. Greyed and unselectable so
|
|
882
|
+
the picker scopes new picks to the variant's family while keeping the
|
|
883
|
+
full palette visible (it isn't gone, it just isn't this gradient's
|
|
884
|
+
business). Pointer cursor is suppressed to match :disabled. */
|
|
885
|
+
.family-item.out-of-family {
|
|
886
|
+
opacity: 0.32;
|
|
887
|
+
cursor: not-allowed;
|
|
888
|
+
}
|
|
889
|
+
.family-item.out-of-family:hover {
|
|
890
|
+
background: none;
|
|
891
|
+
}
|
|
892
|
+
|
|
865
893
|
.family-item.active .family-label {
|
|
866
894
|
color: var(--ui-text-accent);
|
|
867
895
|
}
|
|
@@ -900,7 +928,6 @@
|
|
|
900
928
|
font-family: var(--ui-font-mono);
|
|
901
929
|
color: var(--ui-text-tertiary);
|
|
902
930
|
text-transform: uppercase;
|
|
903
|
-
letter-spacing: 0.04em;
|
|
904
931
|
border-top: 1px solid var(--ui-border-low);
|
|
905
932
|
}
|
|
906
933
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
|
|
4
4
|
interface Props {
|
|
5
|
-
variant?: 'primary' | 'default' | 'secondary';
|
|
5
|
+
variant?: 'primary' | 'default' | 'secondary' | 'outline';
|
|
6
6
|
size?: 'default' | 'compact';
|
|
7
7
|
icon?: string;
|
|
8
8
|
href?: string;
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
aria-disabled={disabled ? 'true' : undefined}
|
|
40
40
|
>
|
|
41
41
|
{#if icon}<i class="fas {icon}" aria-hidden="true"></i>{/if}
|
|
42
|
-
{#if children}{@render children()}{/if}
|
|
42
|
+
{#if children}<span class="ui-pill-label">{@render children()}</span>{/if}
|
|
43
43
|
</a>
|
|
44
44
|
{:else}
|
|
45
45
|
<button
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
{onclick}
|
|
51
51
|
>
|
|
52
52
|
{#if icon}<i class="fas {icon}" aria-hidden="true"></i>{/if}
|
|
53
|
-
{#if children}{@render children()}{/if}
|
|
53
|
+
{#if children}<span class="ui-pill-label">{@render children()}</span>{/if}
|
|
54
54
|
</button>
|
|
55
55
|
{/if}
|
|
56
56
|
|
|
@@ -59,6 +59,8 @@
|
|
|
59
59
|
display: inline-flex;
|
|
60
60
|
align-items: center;
|
|
61
61
|
gap: var(--ui-space-6, 6px);
|
|
62
|
+
max-width: 100%;
|
|
63
|
+
min-width: 0;
|
|
62
64
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.25) 100%);
|
|
63
65
|
border: 1px solid rgba(255, 255, 255, 0.5);
|
|
64
66
|
color: var(--ui-text-primary, #fff);
|
|
@@ -75,11 +77,24 @@
|
|
|
75
77
|
border-color var(--ui-transition-fast, 120ms ease);
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
.ui-pill-label {
|
|
81
|
+
min-width: 0;
|
|
82
|
+
overflow: hidden;
|
|
83
|
+
white-space: nowrap;
|
|
84
|
+
text-overflow: ellipsis;
|
|
85
|
+
}
|
|
86
|
+
|
|
78
87
|
.ui-pill:hover:not(:disabled):not([aria-disabled='true']) {
|
|
79
88
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.45) 100%);
|
|
80
89
|
border-color: rgba(255, 255, 255, 0.75);
|
|
81
90
|
}
|
|
82
91
|
|
|
92
|
+
.ui-pill:focus-visible {
|
|
93
|
+
outline: none;
|
|
94
|
+
border-color: rgba(255, 255, 255, 0.9);
|
|
95
|
+
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.18);
|
|
96
|
+
}
|
|
97
|
+
|
|
83
98
|
.ui-pill:disabled,
|
|
84
99
|
.ui-pill[aria-disabled='true'] {
|
|
85
100
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0.10) 100%);
|
|
@@ -130,6 +145,21 @@
|
|
|
130
145
|
border-color: rgba(255, 255, 255, 0.5);
|
|
131
146
|
}
|
|
132
147
|
|
|
148
|
+
/* Variant: outline — transparent fill, just a border; subtle fill on hover */
|
|
149
|
+
.ui-pill-outline {
|
|
150
|
+
background: none;
|
|
151
|
+
border-color: rgba(255, 255, 255, 0.4);
|
|
152
|
+
}
|
|
153
|
+
.ui-pill-outline:hover:not(:disabled):not([aria-disabled='true']) {
|
|
154
|
+
background: linear-gradient(180deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.08) 100%);
|
|
155
|
+
border-color: rgba(255, 255, 255, 0.65);
|
|
156
|
+
}
|
|
157
|
+
.ui-pill-outline:disabled,
|
|
158
|
+
.ui-pill-outline[aria-disabled='true'] {
|
|
159
|
+
background: none;
|
|
160
|
+
border-color: rgba(255, 255, 255, 0.18);
|
|
161
|
+
}
|
|
162
|
+
|
|
133
163
|
/* Size: compact — for header bars / chrome rails */
|
|
134
164
|
.ui-pill-compact {
|
|
135
165
|
font-size: var(--ui-font-size-sm, 14px);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<script lang="ts" generics="V extends string">
|
|
2
|
+
interface Option {
|
|
3
|
+
value: V;
|
|
4
|
+
label: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
title?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
value: V;
|
|
11
|
+
options: ReadonlyArray<Option>;
|
|
12
|
+
/** Native radio group name. Auto-generated per instance if omitted. */
|
|
13
|
+
name?: string;
|
|
14
|
+
ariaLabel?: string;
|
|
15
|
+
onchange?: (value: V) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let { value = $bindable(), options, name, ariaLabel, onchange }: Props = $props();
|
|
19
|
+
|
|
20
|
+
// Stable per-instance fallback so internal radios group correctly when no
|
|
21
|
+
// `name` is passed. Not reactive: the group identity shouldn't change.
|
|
22
|
+
const fallbackName = `ui-seg-${Math.random().toString(36).slice(2, 9)}`;
|
|
23
|
+
let groupName = $derived(name ?? fallbackName);
|
|
24
|
+
|
|
25
|
+
function select(v: V) {
|
|
26
|
+
if (v === value) return;
|
|
27
|
+
value = v;
|
|
28
|
+
onchange?.(v);
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<div class="ui-seg" role="radiogroup" aria-label={ariaLabel}>
|
|
33
|
+
{#each options as opt (opt.value)}
|
|
34
|
+
<label
|
|
35
|
+
class="ui-seg-item"
|
|
36
|
+
class:active={value === opt.value}
|
|
37
|
+
title={opt.title}
|
|
38
|
+
>
|
|
39
|
+
<input
|
|
40
|
+
type="radio"
|
|
41
|
+
name={groupName}
|
|
42
|
+
value={opt.value}
|
|
43
|
+
checked={value === opt.value}
|
|
44
|
+
onchange={() => select(opt.value)}
|
|
45
|
+
/>
|
|
46
|
+
{#if opt.icon}<i class="fas {opt.icon}" aria-hidden="true"></i>{/if}
|
|
47
|
+
<span>{opt.label}</span>
|
|
48
|
+
</label>
|
|
49
|
+
{/each}
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<style>
|
|
53
|
+
.ui-seg {
|
|
54
|
+
display: inline-flex;
|
|
55
|
+
align-self: flex-start;
|
|
56
|
+
align-items: stretch;
|
|
57
|
+
border: 1px solid rgba(255, 255, 255, 0.5);
|
|
58
|
+
border-radius: var(--ui-radius-xl, 8px);
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.ui-seg-item {
|
|
63
|
+
position: relative;
|
|
64
|
+
display: inline-flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: var(--ui-space-6, 6px);
|
|
67
|
+
padding: var(--ui-space-4, 4px) var(--ui-space-12, 12px);
|
|
68
|
+
color: var(--ui-text-secondary, rgba(255, 255, 255, 0.65));
|
|
69
|
+
font-family: inherit;
|
|
70
|
+
font-size: var(--ui-font-size-sm, 14px);
|
|
71
|
+
font-weight: var(--ui-font-weight-medium, 500);
|
|
72
|
+
line-height: 1.5;
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
user-select: none;
|
|
75
|
+
transition:
|
|
76
|
+
background var(--ui-transition-fast, 120ms ease),
|
|
77
|
+
color var(--ui-transition-fast, 120ms ease);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.ui-seg-item + .ui-seg-item {
|
|
81
|
+
border-left: 1px solid rgba(255, 255, 255, 0.18);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.ui-seg-item input {
|
|
85
|
+
position: absolute;
|
|
86
|
+
opacity: 0;
|
|
87
|
+
pointer-events: none;
|
|
88
|
+
width: 1px;
|
|
89
|
+
height: 1px;
|
|
90
|
+
margin: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.ui-seg-item:hover:not(.active) {
|
|
94
|
+
background: rgba(255, 255, 255, 0.06);
|
|
95
|
+
color: var(--ui-text-primary, #fff);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.ui-seg-item.active {
|
|
99
|
+
background: linear-gradient(180deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.25) 100%);
|
|
100
|
+
color: var(--ui-text-primary, #fff);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.ui-seg-item:has(input:focus-visible) {
|
|
104
|
+
box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.55);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.ui-seg-item i {
|
|
108
|
+
font-size: var(--ui-font-size-xs, 12px);
|
|
109
|
+
color: rgba(255, 255, 255, 0.65);
|
|
110
|
+
}
|
|
111
|
+
.ui-seg-item.active i {
|
|
112
|
+
color: rgba(255, 255, 255, 0.85);
|
|
113
|
+
}
|
|
114
|
+
</style>
|
|
@@ -325,7 +325,10 @@
|
|
|
325
325
|
</div>
|
|
326
326
|
|
|
327
327
|
{#if triggerMeta}
|
|
328
|
-
<span
|
|
328
|
+
<span
|
|
329
|
+
class="ui-ts-meta-text"
|
|
330
|
+
onmouseenter={(e) => { e.currentTarget.title = e.currentTarget.textContent ?? ''; }}
|
|
331
|
+
>{@render triggerMeta()}</span>
|
|
329
332
|
{/if}
|
|
330
333
|
</div>
|
|
331
334
|
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
import OverlaysSection from './sections/OverlaysSection.svelte';
|
|
9
9
|
import GradientsSection from './sections/GradientsSection.svelte';
|
|
10
10
|
import ShadowsSection from './sections/ShadowsSection.svelte';
|
|
11
|
-
import UIPillButton from './UIPillButton.svelte';
|
|
12
11
|
import {
|
|
13
12
|
SPACING_VARS, BORDER_WIDTH_VARS, RADIUS_VARS, FONT_SIZE_VARS,
|
|
14
13
|
ICON_SIZE_VARS, FONT_WEIGHT_VARS, LINE_HEIGHT_VARS,
|
|
@@ -42,8 +41,6 @@
|
|
|
42
41
|
setTimeout(() => { copiedVar = null; }, COPIED_FLASH_MS);
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
type FontAddMode = 'closed' | 'url' | 'fontface';
|
|
46
|
-
let fontAddMode: FontAddMode = $state('closed');
|
|
47
44
|
</script>
|
|
48
45
|
|
|
49
46
|
<div class="variables-container">
|
|
@@ -68,10 +65,16 @@
|
|
|
68
65
|
<!-- Spacing & Borders -->
|
|
69
66
|
<section class="section" id="spacing">
|
|
70
67
|
<h2 class="section-title">Spacing & Borders</h2>
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
<div class="spacing-borders-columns">
|
|
69
|
+
<div class="spacing-borders-group">
|
|
70
|
+
<h3 class="subsection-title">Spacing</h3>
|
|
71
|
+
<TokenScaleTable kind="spacing" vars={SPACING_VARS} {liveVersion} {copiedVar} oncopy={copyVariable} />
|
|
72
|
+
</div>
|
|
73
|
+
<div class="spacing-borders-group">
|
|
74
|
+
<h3 class="subsection-title">Borders</h3>
|
|
75
|
+
<TokenScaleTable kind="border" vars={BORDER_WIDTH_VARS} {liveVersion} {copiedVar} oncopy={copyVariable} />
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
75
78
|
</section>
|
|
76
79
|
|
|
77
80
|
<!-- Columns -->
|
|
@@ -85,18 +88,11 @@
|
|
|
85
88
|
|
|
86
89
|
<!-- Typography -->
|
|
87
90
|
<section class="section" id="typography">
|
|
88
|
-
<
|
|
89
|
-
<h2 class="section-title">Typography</h2>
|
|
90
|
-
<UIPillButton
|
|
91
|
-
variant="primary"
|
|
92
|
-
icon="fa-plus"
|
|
93
|
-
onclick={() => { fontAddMode = fontAddMode === 'closed' ? 'url' : 'closed'; }}
|
|
94
|
-
>Add Font</UIPillButton>
|
|
95
|
-
</div>
|
|
91
|
+
<h2 class="section-title">Typography</h2>
|
|
96
92
|
|
|
97
93
|
<div class="typography-columns">
|
|
98
94
|
<div class="typography-group font-families-group">
|
|
99
|
-
<ProjectFontsSection
|
|
95
|
+
<ProjectFontsSection />
|
|
100
96
|
<h3 class="group-title">Font Families</h3>
|
|
101
97
|
<FontStackEditor />
|
|
102
98
|
</div>
|
|
@@ -161,13 +157,13 @@
|
|
|
161
157
|
.variables-container {
|
|
162
158
|
display: flex;
|
|
163
159
|
flex-direction: column;
|
|
164
|
-
gap: var(--ui-space-
|
|
160
|
+
gap: var(--ui-space-48);
|
|
165
161
|
}
|
|
166
162
|
|
|
167
163
|
.section {
|
|
168
164
|
display: flex;
|
|
169
165
|
flex-direction: column;
|
|
170
|
-
gap: var(--ui-space-
|
|
166
|
+
gap: var(--ui-space-24);
|
|
171
167
|
}
|
|
172
168
|
|
|
173
169
|
.section-title {
|
|
@@ -179,10 +175,12 @@
|
|
|
179
175
|
border-bottom: 2px solid var(--ui-border-high);
|
|
180
176
|
}
|
|
181
177
|
|
|
178
|
+
/* Tier-2 group header: xl bold white. No divider — vertical rhythm carries the
|
|
179
|
+
separation, matching the palette section's title style at a smaller size. */
|
|
182
180
|
.group-title {
|
|
183
|
-
font-size: var(--ui-font-size-
|
|
184
|
-
font-weight: var(--ui-font-weight-
|
|
185
|
-
color: var(--ui-text-
|
|
181
|
+
font-size: var(--ui-font-size-xl);
|
|
182
|
+
font-weight: var(--ui-font-weight-bold);
|
|
183
|
+
color: var(--ui-text-primary);
|
|
186
184
|
margin: 0;
|
|
187
185
|
}
|
|
188
186
|
|
|
@@ -193,27 +191,32 @@
|
|
|
193
191
|
font-weight: var(--ui-font-weight-semibold);
|
|
194
192
|
color: var(--ui-text-tertiary);
|
|
195
193
|
text-transform: uppercase;
|
|
196
|
-
letter-spacing: 0.06em;
|
|
197
194
|
}
|
|
198
195
|
|
|
199
|
-
.subsection-title:first-child
|
|
200
|
-
.section-title + .subsection-title {
|
|
196
|
+
.subsection-title:first-child {
|
|
201
197
|
margin-top: 0;
|
|
202
198
|
}
|
|
203
199
|
|
|
204
|
-
/*
|
|
205
|
-
.
|
|
200
|
+
/* Spacing & Borders columns */
|
|
201
|
+
.spacing-borders-columns {
|
|
202
|
+
display: grid;
|
|
203
|
+
grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr));
|
|
204
|
+
gap: var(--ui-space-24);
|
|
205
|
+
align-items: start;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.spacing-borders-group {
|
|
206
209
|
display: flex;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
padding-bottom: var(--ui-space-8);
|
|
211
|
-
border-bottom: 2px solid var(--ui-border-high);
|
|
210
|
+
flex-direction: column;
|
|
211
|
+
gap: var(--ui-space-8);
|
|
212
|
+
min-width: 0;
|
|
212
213
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
|
|
215
|
+
.spacing-borders-group .subsection-title {
|
|
216
|
+
margin: 0;
|
|
216
217
|
}
|
|
218
|
+
|
|
219
|
+
/* Typography */
|
|
217
220
|
.typography-columns {
|
|
218
221
|
display: grid;
|
|
219
222
|
grid-template-columns: repeat(auto-fill, minmax(min(22rem, 100%), 1fr));
|
|
@@ -224,12 +227,15 @@
|
|
|
224
227
|
.typography-group {
|
|
225
228
|
display: flex;
|
|
226
229
|
flex-direction: column;
|
|
227
|
-
gap: var(--ui-space-
|
|
230
|
+
gap: var(--ui-space-12);
|
|
228
231
|
min-width: 0;
|
|
229
232
|
}
|
|
230
233
|
|
|
231
234
|
.font-families-group {
|
|
232
235
|
grid-column: 1 / -1;
|
|
236
|
+
/* Two logical groups stacked here (Project Fonts + Font Families); space them
|
|
237
|
+
further apart than peers inside a single group. */
|
|
238
|
+
gap: var(--ui-space-32);
|
|
233
239
|
}
|
|
234
240
|
|
|
235
241
|
/* Utility Tokens */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import ScaleCurveEditor from './ScaleCurveEditor.svelte';
|
|
3
|
+
import UIPillButton from '../UIPillButton.svelte';
|
|
3
4
|
import { type CurveAnchor, lightnessCurveConfig, saturationCurveConfig, textLightnessCurveConfig } from '../curveEngine';
|
|
4
5
|
import { scaleToCssVar } from '../../core/palettes/paletteDerivation';
|
|
5
6
|
|
|
@@ -126,19 +127,16 @@
|
|
|
126
127
|
<div class="scale-header">
|
|
127
128
|
<h4 class="scale-title">{scale.title}</h4>
|
|
128
129
|
{#if supportsSnap}
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
type="button"
|
|
130
|
+
<UIPillButton
|
|
131
|
+
size="compact"
|
|
132
|
+
variant={snapped ? 'primary' : 'outline'}
|
|
133
133
|
onclick={() => onToggleSnap(scale)}
|
|
134
|
-
>{snapped ? 'Unsnap' : 'Snap All'}</
|
|
134
|
+
>{snapped ? 'Unsnap' : 'Snap All'}</UIPillButton>
|
|
135
135
|
{/if}
|
|
136
|
-
<
|
|
137
|
-
<
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
onclick={() => onToggleEditor(scale.title)}
|
|
141
|
-
>{editorOpen ? 'Close' : 'Edit'}</button>
|
|
136
|
+
<UIPillButton size="compact" variant="outline" onclick={() => onClearScaleOverrides(scale)}>Clear Overrides</UIPillButton>
|
|
137
|
+
<UIPillButton size="compact" variant="outline" onclick={() => onToggleEditor(scale.title)}>
|
|
138
|
+
{editorOpen ? 'Close' : 'Edit'}
|
|
139
|
+
</UIPillButton>
|
|
142
140
|
</div>
|
|
143
141
|
<div class="swatch-grid" style="--swatch-cols: {scale.steps.length}; --swatch-gap: var(--ui-space-8)">
|
|
144
142
|
{#each scale.steps as step}
|
|
@@ -276,36 +274,15 @@
|
|
|
276
274
|
display: flex;
|
|
277
275
|
align-items: center;
|
|
278
276
|
gap: var(--ui-space-8);
|
|
277
|
+
padding-bottom: 0.5rem;
|
|
279
278
|
}
|
|
280
279
|
|
|
281
280
|
.scale-title {
|
|
282
|
-
font-size: var(--ui-font-size-
|
|
283
|
-
font-weight: var(--ui-font-weight-
|
|
284
|
-
color: var(--ui-text-tertiary);
|
|
285
|
-
margin: 0;
|
|
286
|
-
text-transform: uppercase;
|
|
287
|
-
letter-spacing: 0.05em;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
.edit-toggle {
|
|
291
|
-
font-size: var(--ui-font-size-md);
|
|
292
|
-
color: var(--ui-text-tertiary);
|
|
293
|
-
background: none;
|
|
294
|
-
border: 1px solid var(--ui-border-low);
|
|
295
|
-
border-radius: var(--ui-radius-sm);
|
|
296
|
-
padding: var(--ui-space-2) var(--ui-space-6);
|
|
297
|
-
cursor: pointer;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
.edit-toggle:hover {
|
|
301
|
-
color: var(--ui-text-primary);
|
|
302
|
-
border-color: var(--ui-border-high);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
.edit-toggle.active {
|
|
281
|
+
font-size: var(--ui-font-size-lg);
|
|
282
|
+
font-weight: var(--ui-font-weight-bold);
|
|
306
283
|
color: var(--ui-text-primary);
|
|
307
|
-
|
|
308
|
-
|
|
284
|
+
margin: 0;
|
|
285
|
+
padding-right: 1rem;
|
|
309
286
|
}
|
|
310
287
|
|
|
311
288
|
.swatch-grid {
|
|
@@ -153,13 +153,13 @@
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
.editor-label {
|
|
156
|
-
font-size: var(--ui-font-size-
|
|
156
|
+
font-size: var(--ui-font-size-xl);
|
|
157
157
|
font-weight: var(--ui-font-weight-semibold);
|
|
158
158
|
color: var(--ui-text-primary);
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
.base-hex {
|
|
162
|
-
font-size: var(--ui-font-size-
|
|
162
|
+
font-size: var(--ui-font-size-md);
|
|
163
163
|
color: var(--ui-text-secondary);
|
|
164
164
|
font-family: var(--ui-font-mono);
|
|
165
165
|
}
|
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
padding: var(--ui-space-2) var(--ui-space-4);
|
|
173
173
|
margin-left: calc(-1 * var(--ui-space-4));
|
|
174
174
|
border-radius: var(--ui-radius-sm);
|
|
175
|
-
font-size: var(--ui-font-size-
|
|
175
|
+
font-size: var(--ui-font-size-md);
|
|
176
176
|
color: var(--ui-text-secondary);
|
|
177
177
|
font-family: var(--ui-font-mono);
|
|
178
178
|
text-align: left;
|