@motion-proto/live-tokens 0.1.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +168 -21
- package/dist-plugin/index.cjs +823 -336
- package/dist-plugin/index.d.cts +9 -7
- package/dist-plugin/index.d.ts +9 -7
- package/dist-plugin/index.js +822 -335
- package/package.json +46 -20
- package/src/assets/newspaper.webp +0 -0
- package/src/assets/offering.webp +0 -0
- package/src/component-editor/BadgeEditor.svelte +170 -0
- package/src/component-editor/CalloutEditor.svelte +103 -0
- package/src/component-editor/CardEditor.svelte +184 -0
- package/src/component-editor/CollapsibleSectionEditor.svelte +167 -0
- package/src/component-editor/CornerBadgeEditor.svelte +207 -0
- package/src/component-editor/DialogEditor.svelte +172 -0
- package/src/component-editor/ImageEditor.svelte +72 -0
- package/src/component-editor/InlineEditActionsEditor.svelte +83 -0
- package/src/component-editor/NotificationEditor.svelte +160 -0
- package/src/component-editor/ProgressBarEditor.svelte +124 -0
- package/src/component-editor/RadioButtonEditor.svelte +140 -0
- package/src/component-editor/SectionDividerEditor.svelte +263 -0
- package/src/component-editor/SegmentedControlEditor.svelte +154 -0
- package/src/component-editor/StandardButtonsEditor.svelte +178 -0
- package/src/component-editor/TabBarEditor.svelte +137 -0
- package/src/component-editor/TableEditor.svelte +128 -0
- package/src/component-editor/TooltipEditor.svelte +122 -0
- package/src/component-editor/editorTokens.test.ts +93 -0
- package/src/component-editor/groupKeySlots.test.ts +67 -0
- package/src/component-editor/groupKeySnapshot.test.ts +52 -0
- package/src/component-editor/index.ts +5 -0
- package/src/component-editor/registry.ts +246 -0
- package/src/component-editor/scaffolding/AngleDial.svelte +185 -0
- package/src/component-editor/scaffolding/ComponentEditorBase.svelte +96 -0
- package/src/component-editor/scaffolding/ComponentFileManager.svelte +682 -0
- package/src/component-editor/scaffolding/ComponentFileMenu.svelte +312 -0
- package/src/component-editor/scaffolding/ComponentsTab.svelte +69 -0
- package/src/component-editor/scaffolding/CopyFromMenu.svelte +246 -0
- package/src/component-editor/scaffolding/DemoHeader.svelte +21 -0
- package/src/component-editor/scaffolding/DividerEditor.svelte +81 -0
- package/src/component-editor/scaffolding/FieldsetWrapper.svelte +46 -0
- package/src/component-editor/scaffolding/GradientCard.svelte +291 -0
- package/src/component-editor/scaffolding/LinkageChart.svelte +297 -0
- package/src/component-editor/scaffolding/LinkedBlock.svelte +418 -0
- package/src/component-editor/scaffolding/NonStylableConfig.svelte +57 -0
- package/src/component-editor/scaffolding/SaveAsDialog.svelte +177 -0
- package/src/component-editor/scaffolding/ShadowBackdrop.svelte +25 -0
- package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +56 -0
- package/src/component-editor/scaffolding/StateBlock.svelte +115 -0
- package/src/component-editor/scaffolding/TokenLayout.svelte +511 -0
- package/src/component-editor/scaffolding/TypeEditor.svelte +82 -0
- package/src/component-editor/scaffolding/VariantGroup.svelte +277 -0
- package/src/component-editor/scaffolding/buildTypeGroupTokens.ts +97 -0
- package/src/component-editor/scaffolding/componentSectionType.ts +8 -0
- package/src/component-editor/scaffolding/componentSources.ts +9 -0
- package/src/component-editor/scaffolding/defaultSections.ts +16 -0
- package/src/component-editor/scaffolding/editorContext.ts +44 -0
- package/src/component-editor/scaffolding/linkedBlock.ts +226 -0
- package/src/component-editor/scaffolding/siblings.ts +33 -0
- package/src/component-editor/scaffolding/types.ts +39 -0
- package/src/components/Badge.svelte +231 -42
- package/src/components/Button.svelte +324 -124
- package/src/components/Callout.svelte +145 -0
- package/src/components/Card.svelte +123 -25
- package/src/components/CollapsibleSection.svelte +213 -35
- package/src/components/CornerBadge.svelte +224 -0
- package/src/components/Dialog.svelte +137 -114
- package/src/components/Image.svelte +43 -0
- package/src/components/InlineEditActions.svelte +74 -14
- package/src/components/Notification.svelte +184 -163
- package/src/components/ProgressBar.svelte +216 -22
- package/src/components/RadioButton.svelte +110 -40
- package/src/components/SectionDivider.svelte +428 -74
- package/src/components/SegmentedControl.svelte +203 -0
- package/src/components/TabBar.svelte +146 -21
- package/src/components/Table.svelte +102 -0
- package/src/components/Tooltip.svelte +45 -19
- package/src/components/types.ts +51 -0
- package/src/data/google-fonts.json +75 -0
- package/src/lib/ColumnsOverlay.svelte +20 -7
- package/src/lib/LiveEditorOverlay.svelte +257 -78
- package/src/lib/columnsOverlay.ts +21 -17
- package/src/lib/componentConfig.test.ts +204 -0
- package/src/lib/componentConfigKeys.ts +19 -0
- package/src/lib/componentConfigService.ts +88 -0
- package/src/lib/copyPopover.ts +30 -0
- package/src/lib/cssVarSync.ts +59 -7
- package/src/lib/editorConfigStore.ts +0 -10
- package/src/lib/editorCore.ts +402 -0
- package/src/lib/editorKeybindings.ts +52 -0
- package/src/lib/editorPersistence.ts +106 -0
- package/src/lib/editorRenderer.ts +74 -0
- package/src/lib/editorStore.test.ts +328 -0
- package/src/lib/editorStore.ts +412 -0
- package/src/lib/editorTypes.ts +100 -0
- package/src/lib/editorViewStore.ts +55 -0
- package/src/lib/files/versionedFileResource.ts +140 -0
- package/src/lib/fontLoader.ts +130 -0
- package/src/lib/fontMigration.ts +140 -0
- package/src/lib/fontParse.ts +168 -0
- package/src/lib/index.ts +48 -30
- package/src/lib/lazyConfig.test.ts +54 -0
- package/src/lib/migrations/2026-04-24-component-prefix-and-suffix-renames.ts +64 -0
- package/src/lib/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts +71 -0
- package/src/lib/migrations/2026-04-27-segmentedcontrol-disabled-flatten.ts +43 -0
- package/src/lib/migrations/2026-05-08-collapsiblesection-frame-and-cleanup.ts +68 -0
- package/src/lib/migrations/2026-05-08-collapsiblesection-variant-namespace.ts +35 -0
- package/src/lib/migrations/2026-05-10-sectiondivider-gradient-stops.ts +50 -0
- package/src/lib/migrations/2026-05-13-primary-to-brand.ts +90 -0
- package/src/lib/migrations/index.ts +93 -0
- package/src/lib/migrations/migrations.test.ts +341 -0
- package/src/lib/navLinkTypes.ts +1 -0
- package/src/lib/overlayState.ts +3 -0
- package/src/lib/paletteDerivation.ts +300 -0
- package/src/lib/parentRouteStore.ts +42 -0
- package/src/lib/parsers/globalRootBlock.ts +32 -0
- package/src/lib/presetService.ts +94 -0
- package/src/lib/router.ts +42 -10
- package/src/lib/scrollSection.ts +45 -0
- package/src/lib/slices/columns.ts +59 -0
- package/src/lib/slices/components.ts +362 -0
- package/src/lib/slices/domainVars.ts +15 -0
- package/src/lib/slices/fonts.ts +30 -0
- package/src/lib/slices/gradients.ts +153 -0
- package/src/lib/slices/overlays.ts +132 -0
- package/src/lib/slices/palettes.ts +26 -0
- package/src/lib/slices/shadows.ts +123 -0
- package/src/lib/storage.ts +88 -0
- package/src/lib/themeInit.ts +74 -0
- package/src/lib/themeService.ts +101 -0
- package/src/lib/themeTypes.ts +146 -0
- package/src/lib/tokenRegistry.ts +148 -0
- package/src/pages/ComponentEditorPage.svelte +384 -0
- package/src/pages/ComponentEditorPage.svelte.d.ts +2 -0
- package/src/pages/Editor.svelte +98 -0
- package/src/pages/Editor.svelte.d.ts +2 -0
- package/src/pages/EditorShell.svelte +348 -0
- package/src/styles/_padding.scss +34 -0
- package/src/styles/fonts/Fraunces/Fraunces-italic-latin-ext.woff2 +0 -0
- package/src/styles/fonts/Fraunces/Fraunces-italic-latin.woff2 +0 -0
- package/src/styles/fonts/Fraunces/Fraunces-roman-latin-ext.woff2 +0 -0
- package/src/styles/fonts/Fraunces/Fraunces-roman-latin.woff2 +0 -0
- package/src/styles/fonts/Manrope/Manrope-latin-ext.woff2 +0 -0
- package/src/styles/fonts/Manrope/Manrope-latin.woff2 +0 -0
- package/src/styles/fonts.css +22 -10
- package/src/styles/form-controls.css +14 -16
- package/src/styles/tokens.css +1322 -0
- package/src/styles/ui-editor.css +126 -0
- package/src/{showcase → ui}/BezierCurveEditor.svelte +14 -14
- package/src/{showcase → ui}/ColorEditPanel.svelte +42 -36
- package/src/ui/EditorViewSwitcher.svelte +180 -0
- package/src/ui/FontStackEditor.svelte +360 -0
- package/src/ui/GradientEditor.svelte +461 -0
- package/src/ui/GradientStopPicker.svelte +74 -0
- package/src/ui/PaletteEditor.svelte +1590 -0
- package/src/ui/PaletteEditor.test.ts +108 -0
- package/src/ui/PresetFileManager.svelte +567 -0
- package/src/ui/ProjectFontsSection.svelte +645 -0
- package/src/{showcase → ui}/SurfacesTab.svelte +39 -39
- package/src/{showcase → ui}/TextTab.svelte +27 -27
- package/src/{showcase/TokenFileManager.svelte → ui/ThemeFileManager.svelte} +196 -112
- package/src/ui/Toggle.svelte +108 -0
- package/src/ui/UICopyPopover.svelte +78 -0
- package/src/{showcase/EditorDialog.svelte → ui/UIDialog.svelte} +66 -25
- package/src/ui/UIFontFamilySelector.svelte +309 -0
- package/src/ui/UIFontSizeSelector.svelte +165 -0
- package/src/ui/UIFontWeightSelector.svelte +52 -0
- package/src/ui/UILineHeightSelector.svelte +47 -0
- package/src/ui/UILinkToggle.svelte +60 -0
- package/src/ui/UIOptionItem.svelte +74 -0
- package/src/ui/UIOptionList.svelte +27 -0
- package/src/ui/UIPaddingSelector.svelte +661 -0
- package/src/ui/UIPaletteSelector.svelte +1084 -0
- package/src/ui/UIRadio.svelte +72 -0
- package/src/ui/UIRadioGroup.svelte +59 -0
- package/src/ui/UIRelinkConfirmPopover.svelte +235 -0
- package/src/ui/UITokenSelector.svelte +509 -0
- package/src/ui/UIVariantSelector.svelte +145 -0
- package/src/ui/VariablesTab.svelte +252 -0
- package/src/ui/index.ts +31 -0
- package/src/ui/keepInViewport.ts +84 -0
- package/src/ui/palette/GradientStopEditor.svelte +482 -0
- package/src/ui/palette/OverridesPanel.svelte +526 -0
- package/src/ui/palette/PaletteBase.svelte +165 -0
- package/src/ui/palette/ScaleCurveEditor.svelte +38 -0
- package/src/ui/palette/paletteEditorState.ts +89 -0
- package/src/ui/sections/ColumnsSection.svelte +273 -0
- package/src/ui/sections/GradientsSection.svelte +147 -0
- package/src/ui/sections/OverlaysSection.svelte +670 -0
- package/src/ui/sections/ShadowsSection.svelte +1250 -0
- package/src/ui/sections/TokenScaleTable.svelte +332 -0
- package/src/ui/sections/tokenScales.ts +81 -0
- package/src/ui/variantScales.ts +108 -0
- package/src/components/DetailNav.svelte +0 -78
- package/src/components/Toggle.svelte +0 -86
- package/src/lib/tokenInit.ts +0 -29
- package/src/lib/tokenService.ts +0 -144
- package/src/lib/tokenTypes.ts +0 -45
- package/src/pages/Admin.svelte +0 -100
- package/src/pages/ShowcasePage.svelte +0 -144
- package/src/showcase/BackupBrowser.svelte +0 -617
- package/src/showcase/ComponentsTab.svelte +0 -105
- package/src/showcase/PaletteEditor.svelte +0 -2579
- package/src/showcase/PaletteSelector.svelte +0 -627
- package/src/showcase/TokenMap.svelte +0 -54
- package/src/showcase/VariablesTab.svelte +0 -2655
- package/src/showcase/VisualsTab.svelte +0 -231
- package/src/showcase/demos/BadgeDemo.svelte +0 -56
- package/src/showcase/demos/CardDemo.svelte +0 -50
- package/src/showcase/demos/ChoiceButtonsDemo.svelte +0 -192
- package/src/showcase/demos/CollapsibleSectionDemo.svelte +0 -54
- package/src/showcase/demos/DialogDemo.svelte +0 -42
- package/src/showcase/demos/InlineEditActionsDemo.svelte +0 -25
- package/src/showcase/demos/NotificationDemo.svelte +0 -147
- package/src/showcase/demos/ProgressBarDemo.svelte +0 -54
- package/src/showcase/demos/RadioButtonDemo.svelte +0 -56
- package/src/showcase/demos/SectionDividerDemo.svelte +0 -77
- package/src/showcase/demos/StandardButtonsDemo.svelte +0 -455
- package/src/showcase/demos/TabBarDemo.svelte +0 -58
- package/src/showcase/demos/TooltipDemo.svelte +0 -52
- package/src/showcase/editor.css +0 -93
- package/src/showcase/index.ts +0 -17
- package/src/styles/fonts/Domine/Domine-VariableFont_wght.ttf +0 -0
- package/src/styles/fonts/Domine/OFL.txt +0 -97
- package/src/styles/fonts/Domine/README.txt +0 -66
- /package/src/{showcase → ui}/curveEngine.ts +0 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { get } from 'svelte/store';
|
|
2
|
+
import {
|
|
3
|
+
isComponentPropertyLinked,
|
|
4
|
+
getComponentPropertySiblings,
|
|
5
|
+
editorState,
|
|
6
|
+
} from '../../lib/editorStore';
|
|
7
|
+
import type { CssVarRef } from '../../lib/editorTypes';
|
|
8
|
+
import type { Token } from './types';
|
|
9
|
+
|
|
10
|
+
function aliasKey(ref: CssVarRef | undefined): string {
|
|
11
|
+
if (!ref) return '';
|
|
12
|
+
return ref.kind === 'token' ? `t:${ref.name}` : `v:${ref.value}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const TYPOGRAPHY_PROP_SUFFIXES = ['font-family', 'font-size', 'font-weight', 'line-height'] as const;
|
|
16
|
+
|
|
17
|
+
/** Derive a slot-aware label from a token's groupKey when the groupKey carries
|
|
18
|
+
* a slot prefix (e.g. `title-font-family` → "title font family"). Bare
|
|
19
|
+
* typography groupKeys (`font-family`) and non-typography groupKeys keep the
|
|
20
|
+
* token's original label.
|
|
21
|
+
*
|
|
22
|
+
* Disambiguates rows in the linked block when one bucket holds multiple
|
|
23
|
+
* same-shape link groups (e.g. Notification's title vs. text typography),
|
|
24
|
+
* without coupling the label to the per-variant view. */
|
|
25
|
+
function deriveLinkedLabel(label: string, groupKey: string | undefined): string {
|
|
26
|
+
if (!groupKey) return label;
|
|
27
|
+
for (const prop of TYPOGRAPHY_PROP_SUFFIXES) {
|
|
28
|
+
if (groupKey === prop) return label;
|
|
29
|
+
if (groupKey.endsWith('-' + prop)) {
|
|
30
|
+
const slot = groupKey.slice(0, groupKey.length - prop.length - 1);
|
|
31
|
+
return `${slot} ${label}`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return label;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** `Token` enriched by the linked-block computation. The base fields are inherited from
|
|
38
|
+
`Token`; the extra commentary on `mergeVariables` here is linked-block-specific. */
|
|
39
|
+
export interface LinkedToken extends Token {
|
|
40
|
+
/** Other groupKey lead variables whose current alias matches this row's. The row writes
|
|
41
|
+
the same alias to each of these (and their siblings) so the merged display stays in sync. */
|
|
42
|
+
mergeVariables?: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type LinkedGroup = {
|
|
46
|
+
token: LinkedToken;
|
|
47
|
+
/** Full set of contexts participating in this group (linked + broken), ordered by the
|
|
48
|
+
caller's `linkableContexts` insertion order so the LinkageChart row order matches the
|
|
49
|
+
variant tab strip. `brokenContexts` is a subset; the difference is currently linked. */
|
|
50
|
+
contexts: string[];
|
|
51
|
+
/** Subset of `contexts` whose alias has been overridden out of the linked group.
|
|
52
|
+
Renders as broken cells in the LinkageChart so the historical relationship stays visible. */
|
|
53
|
+
brokenContexts: string[];
|
|
54
|
+
variables: string[];
|
|
55
|
+
linked: boolean;
|
|
56
|
+
/** Reverse lookup from chart context label (e.g. "primary default") to the sibling variable
|
|
57
|
+
that backs that cell. Used by LinkedBlock to swap the row's displayed variable when the
|
|
58
|
+
user focuses a different variant/state, so the value and pop-bar reflect the cell the
|
|
59
|
+
user is looking at — not the group's first-seen representative. */
|
|
60
|
+
contextToVariable: Map<string, string>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type LinkedBlockResult = {
|
|
64
|
+
groups: LinkedGroup[];
|
|
65
|
+
varSet: Set<string>;
|
|
66
|
+
contextsByVar: Record<string, string[]>;
|
|
67
|
+
linkedOrder: Map<string, number>;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Compute linked-block groups for the given component.
|
|
72
|
+
* Each entry in `linkableContexts` maps a representative variable to a context label.
|
|
73
|
+
* A group is formed when ≥2 sibling variables (same component, same groupKey) exist.
|
|
74
|
+
*
|
|
75
|
+
* Reads editor state internally via `getComponentPropertySiblings` (which `get`s the store).
|
|
76
|
+
* Pass `$editorState` as the final argument so the call site subscribes and Svelte re-runs
|
|
77
|
+
* the reactive statement when state changes. The argument itself is ignored:
|
|
78
|
+
*
|
|
79
|
+
* $: linked = computeLinkedBlock(component, linkableContexts, allTokens, $editorState);
|
|
80
|
+
*/
|
|
81
|
+
export function computeLinkedBlock(
|
|
82
|
+
component: string,
|
|
83
|
+
linkableContexts: Map<string, string>,
|
|
84
|
+
allTokens: LinkedToken[],
|
|
85
|
+
// Reactivity hook — see JSDoc. The runtime state is read via `get(store)` internally;
|
|
86
|
+
// this parameter exists only so callers can pass `$editorState` to create the subscription.
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
88
|
+
_stateForReactivity?: unknown,
|
|
89
|
+
): LinkedBlockResult {
|
|
90
|
+
const groups: LinkedGroup[] = [];
|
|
91
|
+
const varSet = new Set<string>();
|
|
92
|
+
const seen = new Set<string>();
|
|
93
|
+
const seenGroupKeys = new Set<string>();
|
|
94
|
+
const tokensByVar = new Map(allTokens.map((t) => [t.variable, t]));
|
|
95
|
+
|
|
96
|
+
// Topology peers per groupKey (everything the editor declared as part of one sharing set).
|
|
97
|
+
// Used to mirror writes from a row's lead onto sibling variables that aren't yet in the
|
|
98
|
+
// slice, so a single user change propagates to the full declared topology.
|
|
99
|
+
const topologyByGroupKey = new Map<string, string[]>();
|
|
100
|
+
for (const [variable] of linkableContexts) {
|
|
101
|
+
const gk = tokensByVar.get(variable)?.groupKey;
|
|
102
|
+
if (!gk) continue;
|
|
103
|
+
const list = topologyByGroupKey.get(gk) ?? [];
|
|
104
|
+
list.push(variable);
|
|
105
|
+
topologyByGroupKey.set(gk, list);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (const [variable] of linkableContexts) {
|
|
109
|
+
if (seen.has(variable)) continue;
|
|
110
|
+
const rep = tokensByVar.get(variable);
|
|
111
|
+
if (!rep) continue;
|
|
112
|
+
if (rep.groupKey && seenGroupKeys.has(rep.groupKey)) continue;
|
|
113
|
+
const siblings = getComponentPropertySiblings(component, variable);
|
|
114
|
+
if (siblings.length < 2) continue;
|
|
115
|
+
for (const s of siblings) seen.add(s);
|
|
116
|
+
if (rep.groupKey) {
|
|
117
|
+
seenGroupKeys.add(rep.groupKey);
|
|
118
|
+
// Mark every other declared peer in linkableContexts as seen so they don't spawn dupe groups
|
|
119
|
+
// when the slice covers only a subset of the declared topology.
|
|
120
|
+
for (const peer of topologyByGroupKey.get(rep.groupKey) ?? []) seen.add(peer);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const propertyLinked = isComponentPropertyLinked(component, variable);
|
|
124
|
+
const declaredPeers = rep.groupKey ? topologyByGroupKey.get(rep.groupKey) ?? [] : [];
|
|
125
|
+
const mergePeers = declaredPeers.filter((v) => v !== variable && !siblings.includes(v));
|
|
126
|
+
|
|
127
|
+
// Partition the *currently linked* siblings by alias. Explicitly unlinked
|
|
128
|
+
// siblings (those in `slice.unlinked`) are tracked separately and always
|
|
129
|
+
// render as broken in the chart — even when their alias happens to match
|
|
130
|
+
// the canonical value — because they've opted out of sharing.
|
|
131
|
+
const slice = get(editorState).components[component];
|
|
132
|
+
const aliases = slice?.aliases ?? {};
|
|
133
|
+
const unlinkedSet = new Set(slice?.unlinked ?? []);
|
|
134
|
+
const buckets = new Map<string, string[]>();
|
|
135
|
+
for (const s of siblings) {
|
|
136
|
+
if (unlinkedSet.has(s)) continue;
|
|
137
|
+
const k = aliasKey(aliases[s]);
|
|
138
|
+
const arr = buckets.get(k) ?? [];
|
|
139
|
+
arr.push(s);
|
|
140
|
+
buckets.set(k, arr);
|
|
141
|
+
}
|
|
142
|
+
// Canonical = representative's bucket (if it's linked). Promoted to a more
|
|
143
|
+
// populous *explicit-alias* bucket when one exists. The empty-key bucket
|
|
144
|
+
// (peers with no saved alias) only wins canonical when the representative
|
|
145
|
+
// itself has no saved alias — otherwise an unsaved peer would silently
|
|
146
|
+
// outvote a peer that the user actively set.
|
|
147
|
+
let canonicalKey = unlinkedSet.has(variable) ? '' : aliasKey(aliases[variable]);
|
|
148
|
+
let canonicalSize = buckets.get(canonicalKey)?.length ?? 0;
|
|
149
|
+
for (const [k, arr] of buckets) {
|
|
150
|
+
if (k === '' && canonicalKey !== '') continue;
|
|
151
|
+
if (arr.length > canonicalSize) {
|
|
152
|
+
canonicalKey = k;
|
|
153
|
+
canonicalSize = arr.length;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const linkedSiblings = buckets.get(canonicalKey) ?? [];
|
|
157
|
+
const divergedSiblings: string[] = [];
|
|
158
|
+
for (const [k, arr] of buckets) {
|
|
159
|
+
if (k !== canonicalKey) divergedSiblings.push(...arr);
|
|
160
|
+
}
|
|
161
|
+
const explicitlyUnlinked = siblings.filter((s) => unlinkedSet.has(s));
|
|
162
|
+
|
|
163
|
+
// Build context lists in canonical (linkableContexts insertion) order so the
|
|
164
|
+
// LinkageChart row order matches the variant tab strip the editor declared.
|
|
165
|
+
// `contextToVariable` records which sibling backs each cell, so LinkedBlock
|
|
166
|
+
// can swap the row's displayed variable to follow focusedVariant/focusedState.
|
|
167
|
+
const linkedSet = new Set(linkedSiblings);
|
|
168
|
+
const brokenVarSet = new Set([...divergedSiblings, ...mergePeers, ...explicitlyUnlinked]);
|
|
169
|
+
const siblingSet = new Set(siblings);
|
|
170
|
+
const seenAll = new Set<string>();
|
|
171
|
+
const seenBroken = new Set<string>();
|
|
172
|
+
const ctxs: string[] = [];
|
|
173
|
+
const brokenContexts: string[] = [];
|
|
174
|
+
const contextToVariable = new Map<string, string>();
|
|
175
|
+
for (const [peer, ctx] of linkableContexts) {
|
|
176
|
+
if (rep.groupKey && tokensByVar.get(peer)?.groupKey !== rep.groupKey) continue;
|
|
177
|
+
if (siblingSet.has(peer) && !contextToVariable.has(ctx)) {
|
|
178
|
+
contextToVariable.set(ctx, peer);
|
|
179
|
+
}
|
|
180
|
+
const isLinked = linkedSet.has(peer);
|
|
181
|
+
const isBroken = brokenVarSet.has(peer);
|
|
182
|
+
if (!isLinked && !isBroken) continue;
|
|
183
|
+
if (!seenAll.has(ctx)) {
|
|
184
|
+
seenAll.add(ctx);
|
|
185
|
+
ctxs.push(ctx);
|
|
186
|
+
}
|
|
187
|
+
if (isBroken && !seenBroken.has(ctx)) {
|
|
188
|
+
seenBroken.add(ctx);
|
|
189
|
+
brokenContexts.push(ctx);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const tok: LinkedToken = {
|
|
193
|
+
...rep,
|
|
194
|
+
label: deriveLinkedLabel(rep.label, rep.groupKey),
|
|
195
|
+
canBeLinked: true,
|
|
196
|
+
...(mergePeers.length ? { mergeVariables: mergePeers } : {}),
|
|
197
|
+
};
|
|
198
|
+
groups.push({ token: tok, contexts: ctxs, brokenContexts, variables: siblings, linked: propertyLinked, contextToVariable });
|
|
199
|
+
// Per-variant rows for linked siblings stay directly editable — writes fan
|
|
200
|
+
// out through the linked write path (`setComponentAliasLinked`) because
|
|
201
|
+
// `isLinkedDisplay` resolves true. The link topology is dev-declared, so
|
|
202
|
+
// we don't gate edits behind a "go use the linked block" detour; the
|
|
203
|
+
// linked-block row is the topology view, not the only edit point.
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const contextsByVar = Object.fromEntries(groups.map((g) => [g.token.variable, g.contexts]));
|
|
207
|
+
const linkedOrder = new Map<string, number>(
|
|
208
|
+
groups.flatMap((g, i) => [
|
|
209
|
+
[g.token.variable, i] as const,
|
|
210
|
+
...g.variables.map((v) => [v, i] as const),
|
|
211
|
+
]),
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
return { groups, varSet, contextsByVar, linkedOrder };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** No-op since the linked-block became a peer to per-variant editing rather
|
|
218
|
+
* than the single canonical edit point. Per-variant rows for linked properties
|
|
219
|
+
* stay fully interactive — writes route through `setComponentAliasLinked`
|
|
220
|
+
* when `isLinkedDisplay` resolves true, fanning out to all linked siblings.
|
|
221
|
+
*
|
|
222
|
+
* Kept as an identity passthrough so existing editor call sites compile
|
|
223
|
+
* without churn; remove the calls (and this export) when consolidating. */
|
|
224
|
+
export function withLinkedDisabled<T extends { variable: string }>(tokens: T[], _linked: Set<string>): T[] {
|
|
225
|
+
return tokens;
|
|
226
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Token, TypeGroupConfig } from './types';
|
|
2
|
+
|
|
3
|
+
export type Sibling = {
|
|
4
|
+
name: string;
|
|
5
|
+
label: string;
|
|
6
|
+
states: Record<string, Token[]>;
|
|
7
|
+
typeGroups?: Record<string, TypeGroupConfig[]>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/** Build the `siblings` list a VariantGroup needs for its "Copy from" menu.
|
|
11
|
+
Given the full variant list and per-variant state-map builders, returns
|
|
12
|
+
every variant *except* `toVariant` shaped as a Sibling.
|
|
13
|
+
|
|
14
|
+
`variantStates(v)` and `variantTypeGroups(v)` return the same shape the
|
|
15
|
+
parent VariantGroup gets for its own `states` / `typeGroups` props — a map
|
|
16
|
+
keyed by state name. For single-state-per-variant editors (Badge, Notification,
|
|
17
|
+
ProgressBar), wrap the single-state builders inline:
|
|
18
|
+
`(v) => ({ [v]: variantTokens(v) })`. */
|
|
19
|
+
export function buildSiblings<V extends string>(
|
|
20
|
+
variants: readonly V[],
|
|
21
|
+
toVariant: V,
|
|
22
|
+
variantStates: (v: V) => Record<string, Token[]>,
|
|
23
|
+
variantTypeGroups?: (v: V) => Record<string, TypeGroupConfig[]>,
|
|
24
|
+
): Sibling[] {
|
|
25
|
+
return variants
|
|
26
|
+
.filter((v) => v !== toVariant)
|
|
27
|
+
.map((v) => ({
|
|
28
|
+
name: v,
|
|
29
|
+
label: v.charAt(0).toUpperCase() + v.slice(1),
|
|
30
|
+
states: variantStates(v),
|
|
31
|
+
typeGroups: variantTypeGroups ? variantTypeGroups(v) : undefined,
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Editor token: a single CSS custom property the user can theme. */
|
|
2
|
+
export type Token = {
|
|
3
|
+
label: string;
|
|
4
|
+
variable: string;
|
|
5
|
+
/** When true, this token participates in the linked block when ≥2 variants agree on its value. */
|
|
6
|
+
canBeLinked?: boolean;
|
|
7
|
+
/** Used by the editor store to identify cross-variant counterparts (e.g. all `border-width` tokens link). */
|
|
8
|
+
groupKey?: string;
|
|
9
|
+
/** Token row is rendered in a disabled state (still visible). */
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
/** Token row is omitted entirely. */
|
|
12
|
+
hidden?: boolean;
|
|
13
|
+
/** When the linked block collapses several same-label same-value rows into one,
|
|
14
|
+
the surviving row carries the other groupKey leads here so writes co-propagate. */
|
|
15
|
+
mergeVariables?: string[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** Editor type-group: a fieldset containing a coordinated set of typography tokens
|
|
19
|
+
(text color + font-family/size/weight/line-height) for a piece of content
|
|
20
|
+
(e.g. a card title, notification body). Optional outline rows let
|
|
21
|
+
text-with-stroke groups (e.g. SectionDivider title) keep their stroke
|
|
22
|
+
width/color visually nested with the typography that drives them. */
|
|
23
|
+
export type TypeGroupConfig = {
|
|
24
|
+
legend?: string;
|
|
25
|
+
colorVariable: string;
|
|
26
|
+
colorLabel?: string;
|
|
27
|
+
familyVariable?: string;
|
|
28
|
+
familyLabel?: string;
|
|
29
|
+
sizeVariable?: string;
|
|
30
|
+
sizeLabel?: string;
|
|
31
|
+
weightVariable?: string;
|
|
32
|
+
weightLabel?: string;
|
|
33
|
+
lineHeightVariable?: string;
|
|
34
|
+
lineHeightLabel?: string;
|
|
35
|
+
outlineWidthVariable?: string;
|
|
36
|
+
outlineWidthLabel?: string;
|
|
37
|
+
outlineColorVariable?: string;
|
|
38
|
+
outlineColorLabel?: string;
|
|
39
|
+
};
|
|
@@ -1,10 +1,41 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
export const badgeVariants = [
|
|
3
|
+
'primary',
|
|
4
|
+
'accent',
|
|
5
|
+
'neutral',
|
|
6
|
+
'alternate',
|
|
7
|
+
'canvas',
|
|
8
|
+
'special',
|
|
9
|
+
'success',
|
|
10
|
+
'warning',
|
|
11
|
+
'danger',
|
|
12
|
+
'info',
|
|
13
|
+
] as const;
|
|
14
|
+
export type BadgeVariant = typeof badgeVariants[number];
|
|
15
|
+
</script>
|
|
16
|
+
|
|
1
17
|
<script lang="ts">
|
|
2
|
-
export let variant:
|
|
18
|
+
export let variant: BadgeVariant = 'info';
|
|
3
19
|
export let size: 'default' | 'small' = 'default';
|
|
4
20
|
export let icon: string | undefined = undefined;
|
|
21
|
+
/** When true, badge is absolutely positioned. Requires the parent to be position: relative. */
|
|
22
|
+
export let floating: boolean = false;
|
|
23
|
+
/** Corner of the positioned parent to attach to when floating. */
|
|
24
|
+
export let anchor: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' = 'bottom-right';
|
|
25
|
+
/** CSS length to offset from the anchor edges. Defaults to --space-12 (or --space-8 when size='small'). */
|
|
26
|
+
export let offset: string | undefined = undefined;
|
|
27
|
+
/** When true, badge sits flush in the anchor corner: zero offset and squared corners. */
|
|
28
|
+
export let flush: boolean = false;
|
|
5
29
|
</script>
|
|
6
30
|
|
|
7
|
-
<span
|
|
31
|
+
<span
|
|
32
|
+
class="badge badge-{variant}"
|
|
33
|
+
class:badge-small={size === 'small'}
|
|
34
|
+
class:badge-floating={floating}
|
|
35
|
+
class:badge-flush={flush}
|
|
36
|
+
data-anchor={floating ? anchor : undefined}
|
|
37
|
+
style={floating && offset && !flush ? `--badge-offset: ${offset};` : undefined}
|
|
38
|
+
>
|
|
8
39
|
{#if icon}
|
|
9
40
|
<span class="icon"><i class={icon}></i></span>
|
|
10
41
|
{:else}
|
|
@@ -13,24 +44,185 @@
|
|
|
13
44
|
<slot />
|
|
14
45
|
</span>
|
|
15
46
|
|
|
16
|
-
<style>
|
|
47
|
+
<style lang="scss">
|
|
48
|
+
@use '../styles/padding' as *;
|
|
49
|
+
|
|
50
|
+
$variants: info, accent, primary, success, warning, danger, neutral, special, alternate, canvas;
|
|
51
|
+
|
|
52
|
+
// Per-variant token block kept flat (not collapsed via SCSS @each) so the
|
|
53
|
+
// Layer-2 token-discovery parser (`extractGlobalRootBody` in
|
|
54
|
+
// src/lib/parsers/globalRootBlock.ts) can read the .svelte source verbatim;
|
|
55
|
+
// @each interpolation would make the parser see zero tokens for Badge,
|
|
56
|
+
// even though the rendered DOM would be identical. See parallel comment in
|
|
57
|
+
// Notification.svelte. `--text-primary` is the neutral primary text (with
|
|
58
|
+
// -secondary/-tertiary scale); `--text-brand` is the brand-family text.
|
|
59
|
+
:global(:root) {
|
|
60
|
+
/* Primary */
|
|
61
|
+
--badge-primary-surface: var(--surface-brand);
|
|
62
|
+
--badge-primary-text: var(--text-brand);
|
|
63
|
+
--badge-primary-border: var(--border-brand);
|
|
64
|
+
--badge-primary-text-font-family: var(--font-sans);
|
|
65
|
+
--badge-primary-text-font-size: var(--font-size-sm);
|
|
66
|
+
--badge-primary-text-font-weight: var(--font-weight-light);
|
|
67
|
+
--badge-primary-text-line-height: var(--line-height-tight);
|
|
68
|
+
--badge-primary-border-width: var(--border-width-1);
|
|
69
|
+
--badge-primary-radius: var(--radius-full);
|
|
70
|
+
--badge-primary-padding: var(--space-6);
|
|
71
|
+
--badge-primary-shadow: var(--shadow-none);
|
|
72
|
+
--badge-primary-blur: var(--blur-none);
|
|
73
|
+
--badge-primary-icon-size: var(--icon-size-sm);
|
|
74
|
+
|
|
75
|
+
/* Accent */
|
|
76
|
+
--badge-accent-surface: var(--surface-accent);
|
|
77
|
+
--badge-accent-text: var(--text-accent);
|
|
78
|
+
--badge-accent-border: var(--border-accent);
|
|
79
|
+
--badge-accent-text-font-family: var(--font-sans);
|
|
80
|
+
--badge-accent-text-font-size: var(--font-size-sm);
|
|
81
|
+
--badge-accent-text-font-weight: var(--font-weight-light);
|
|
82
|
+
--badge-accent-text-line-height: var(--line-height-tight);
|
|
83
|
+
--badge-accent-border-width: var(--border-width-1);
|
|
84
|
+
--badge-accent-radius: var(--radius-full);
|
|
85
|
+
--badge-accent-padding: var(--space-6);
|
|
86
|
+
--badge-accent-shadow: var(--shadow-none);
|
|
87
|
+
--badge-accent-blur: var(--blur-none);
|
|
88
|
+
--badge-accent-icon-size: var(--icon-size-sm);
|
|
89
|
+
|
|
90
|
+
/* Neutral */
|
|
91
|
+
--badge-neutral-surface: var(--surface-neutral);
|
|
92
|
+
--badge-neutral-text: var(--text-primary);
|
|
93
|
+
--badge-neutral-border: var(--border-neutral);
|
|
94
|
+
--badge-neutral-text-font-family: var(--font-sans);
|
|
95
|
+
--badge-neutral-text-font-size: var(--font-size-sm);
|
|
96
|
+
--badge-neutral-text-font-weight: var(--font-weight-light);
|
|
97
|
+
--badge-neutral-text-line-height: var(--line-height-tight);
|
|
98
|
+
--badge-neutral-border-width: var(--border-width-1);
|
|
99
|
+
--badge-neutral-radius: var(--radius-full);
|
|
100
|
+
--badge-neutral-padding: var(--space-6);
|
|
101
|
+
--badge-neutral-shadow: var(--shadow-none);
|
|
102
|
+
--badge-neutral-blur: var(--blur-none);
|
|
103
|
+
--badge-neutral-icon-size: var(--icon-size-sm);
|
|
104
|
+
|
|
105
|
+
/* Alternate */
|
|
106
|
+
--badge-alternate-surface: var(--surface-alternate);
|
|
107
|
+
--badge-alternate-text: var(--text-alternate);
|
|
108
|
+
--badge-alternate-border: var(--border-alternate);
|
|
109
|
+
--badge-alternate-text-font-family: var(--font-sans);
|
|
110
|
+
--badge-alternate-text-font-size: var(--font-size-sm);
|
|
111
|
+
--badge-alternate-text-font-weight: var(--font-weight-light);
|
|
112
|
+
--badge-alternate-text-line-height: var(--line-height-tight);
|
|
113
|
+
--badge-alternate-border-width: var(--border-width-1);
|
|
114
|
+
--badge-alternate-radius: var(--radius-full);
|
|
115
|
+
--badge-alternate-padding: var(--space-6);
|
|
116
|
+
--badge-alternate-shadow: var(--shadow-none);
|
|
117
|
+
--badge-alternate-blur: var(--blur-none);
|
|
118
|
+
--badge-alternate-icon-size: var(--icon-size-sm);
|
|
119
|
+
|
|
120
|
+
/* Canvas */
|
|
121
|
+
--badge-canvas-surface: var(--surface-canvas);
|
|
122
|
+
--badge-canvas-text: var(--text-canvas);
|
|
123
|
+
--badge-canvas-border: var(--border-canvas);
|
|
124
|
+
--badge-canvas-text-font-family: var(--font-sans);
|
|
125
|
+
--badge-canvas-text-font-size: var(--font-size-sm);
|
|
126
|
+
--badge-canvas-text-font-weight: var(--font-weight-light);
|
|
127
|
+
--badge-canvas-text-line-height: var(--line-height-tight);
|
|
128
|
+
--badge-canvas-border-width: var(--border-width-1);
|
|
129
|
+
--badge-canvas-radius: var(--radius-full);
|
|
130
|
+
--badge-canvas-padding: var(--space-6);
|
|
131
|
+
--badge-canvas-shadow: var(--shadow-none);
|
|
132
|
+
--badge-canvas-blur: var(--blur-none);
|
|
133
|
+
--badge-canvas-icon-size: var(--icon-size-sm);
|
|
134
|
+
|
|
135
|
+
/* Special */
|
|
136
|
+
--badge-special-surface: var(--surface-special);
|
|
137
|
+
--badge-special-text: var(--text-special);
|
|
138
|
+
--badge-special-border: var(--border-special);
|
|
139
|
+
--badge-special-text-font-family: var(--font-sans);
|
|
140
|
+
--badge-special-text-font-size: var(--font-size-sm);
|
|
141
|
+
--badge-special-text-font-weight: var(--font-weight-light);
|
|
142
|
+
--badge-special-text-line-height: var(--line-height-tight);
|
|
143
|
+
--badge-special-border-width: var(--border-width-1);
|
|
144
|
+
--badge-special-radius: var(--radius-full);
|
|
145
|
+
--badge-special-padding: var(--space-6);
|
|
146
|
+
--badge-special-shadow: var(--shadow-none);
|
|
147
|
+
--badge-special-blur: var(--blur-none);
|
|
148
|
+
--badge-special-icon-size: var(--icon-size-sm);
|
|
149
|
+
|
|
150
|
+
/* Success */
|
|
151
|
+
--badge-success-surface: var(--surface-success);
|
|
152
|
+
--badge-success-text: var(--text-success);
|
|
153
|
+
--badge-success-border: var(--border-success);
|
|
154
|
+
--badge-success-text-font-family: var(--font-sans);
|
|
155
|
+
--badge-success-text-font-size: var(--font-size-sm);
|
|
156
|
+
--badge-success-text-font-weight: var(--font-weight-light);
|
|
157
|
+
--badge-success-text-line-height: var(--line-height-tight);
|
|
158
|
+
--badge-success-border-width: var(--border-width-1);
|
|
159
|
+
--badge-success-radius: var(--radius-full);
|
|
160
|
+
--badge-success-padding: var(--space-6);
|
|
161
|
+
--badge-success-shadow: var(--shadow-none);
|
|
162
|
+
--badge-success-blur: var(--blur-none);
|
|
163
|
+
--badge-success-icon-size: var(--icon-size-sm);
|
|
164
|
+
|
|
165
|
+
/* Warning */
|
|
166
|
+
--badge-warning-surface: var(--surface-warning);
|
|
167
|
+
--badge-warning-text: var(--text-warning);
|
|
168
|
+
--badge-warning-border: var(--border-warning);
|
|
169
|
+
--badge-warning-text-font-family: var(--font-sans);
|
|
170
|
+
--badge-warning-text-font-size: var(--font-size-sm);
|
|
171
|
+
--badge-warning-text-font-weight: var(--font-weight-light);
|
|
172
|
+
--badge-warning-text-line-height: var(--line-height-tight);
|
|
173
|
+
--badge-warning-border-width: var(--border-width-1);
|
|
174
|
+
--badge-warning-radius: var(--radius-full);
|
|
175
|
+
--badge-warning-padding: var(--space-6);
|
|
176
|
+
--badge-warning-shadow: var(--shadow-none);
|
|
177
|
+
--badge-warning-blur: var(--blur-none);
|
|
178
|
+
--badge-warning-icon-size: var(--icon-size-sm);
|
|
179
|
+
|
|
180
|
+
/* Danger */
|
|
181
|
+
--badge-danger-surface: var(--surface-danger);
|
|
182
|
+
--badge-danger-text: var(--text-danger);
|
|
183
|
+
--badge-danger-border: var(--border-danger);
|
|
184
|
+
--badge-danger-text-font-family: var(--font-sans);
|
|
185
|
+
--badge-danger-text-font-size: var(--font-size-sm);
|
|
186
|
+
--badge-danger-text-font-weight: var(--font-weight-light);
|
|
187
|
+
--badge-danger-text-line-height: var(--line-height-tight);
|
|
188
|
+
--badge-danger-border-width: var(--border-width-1);
|
|
189
|
+
--badge-danger-radius: var(--radius-full);
|
|
190
|
+
--badge-danger-padding: var(--space-6);
|
|
191
|
+
--badge-danger-shadow: var(--shadow-none);
|
|
192
|
+
--badge-danger-blur: var(--blur-none);
|
|
193
|
+
--badge-danger-icon-size: var(--icon-size-sm);
|
|
194
|
+
|
|
195
|
+
/* Info */
|
|
196
|
+
--badge-info-surface: var(--surface-info);
|
|
197
|
+
--badge-info-text: var(--text-info);
|
|
198
|
+
--badge-info-border: var(--border-info);
|
|
199
|
+
--badge-info-text-font-family: var(--font-sans);
|
|
200
|
+
--badge-info-text-font-size: var(--font-size-sm);
|
|
201
|
+
--badge-info-text-font-weight: var(--font-weight-light);
|
|
202
|
+
--badge-info-text-line-height: var(--line-height-tight);
|
|
203
|
+
--badge-info-border-width: var(--border-width-1);
|
|
204
|
+
--badge-info-radius: var(--radius-full);
|
|
205
|
+
--badge-info-padding: var(--space-6);
|
|
206
|
+
--badge-info-shadow: var(--shadow-none);
|
|
207
|
+
--badge-info-blur: var(--blur-none);
|
|
208
|
+
--badge-info-icon-size: var(--icon-size-sm);
|
|
209
|
+
}
|
|
210
|
+
|
|
17
211
|
.badge {
|
|
212
|
+
--badge-offset: var(--space-12);
|
|
18
213
|
display: inline-flex;
|
|
19
214
|
align-items: center;
|
|
20
215
|
gap: var(--space-6);
|
|
21
|
-
|
|
22
|
-
line-height: 1;
|
|
23
|
-
padding: var(--space-6) var(--space-12);
|
|
24
|
-
border-radius: var(--radius-full);
|
|
25
|
-
font-family: var(--font-sans);
|
|
26
|
-
font-weight: var(--font-weight-medium);
|
|
216
|
+
line-height: var(--line-height-tight);
|
|
27
217
|
white-space: nowrap;
|
|
218
|
+
backdrop-filter: blur(var(--badge-blur, 0));
|
|
219
|
+
-webkit-backdrop-filter: blur(var(--badge-blur, 0));
|
|
28
220
|
}
|
|
29
221
|
|
|
30
222
|
.icon {
|
|
31
223
|
display: inline-flex;
|
|
32
224
|
align-items: center;
|
|
33
|
-
font-size: 1em;
|
|
225
|
+
font-size: var(--badge-icon-size, 1em);
|
|
34
226
|
}
|
|
35
227
|
|
|
36
228
|
.icon:empty {
|
|
@@ -38,45 +230,42 @@
|
|
|
38
230
|
}
|
|
39
231
|
|
|
40
232
|
.badge-small {
|
|
41
|
-
|
|
42
|
-
padding: var(--space-6) var(--space-12);
|
|
233
|
+
--badge-offset: var(--space-8);
|
|
43
234
|
gap: var(--space-4);
|
|
44
235
|
}
|
|
45
236
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
box-shadow: var(--shadow-sm);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
/* Accent */
|
|
56
|
-
.badge-accent {
|
|
57
|
-
color: var(--color-accent-300);
|
|
58
|
-
background: var(--surface-neutral-higher);
|
|
59
|
-
border: 1px solid var(--border-accent);
|
|
60
|
-
box-shadow: var(--shadow-sm);
|
|
61
|
-
text-transform: capitalize;
|
|
237
|
+
.badge-floating {
|
|
238
|
+
position: absolute;
|
|
239
|
+
/* Floating badges are decorative overlays over other content — let clicks
|
|
240
|
+
pass through to whatever owns the underlying surface (links, buttons). */
|
|
241
|
+
pointer-events: none;
|
|
62
242
|
}
|
|
63
243
|
|
|
244
|
+
.badge-floating[data-anchor='top-left'] { top: var(--badge-offset); left: var(--badge-offset); }
|
|
245
|
+
.badge-floating[data-anchor='top-right'] { top: var(--badge-offset); right: var(--badge-offset); }
|
|
246
|
+
.badge-floating[data-anchor='bottom-left'] { bottom: var(--badge-offset); left: var(--badge-offset); }
|
|
247
|
+
.badge-floating[data-anchor='bottom-right'] { bottom: var(--badge-offset); right: var(--badge-offset); }
|
|
64
248
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
249
|
+
@each $v in $variants {
|
|
250
|
+
.badge-#{$v} {
|
|
251
|
+
--badge-icon-size: var(--badge-#{$v}-icon-size);
|
|
252
|
+
--badge-blur: var(--badge-#{$v}-blur);
|
|
253
|
+
color: var(--badge-#{$v}-text);
|
|
254
|
+
background: var(--badge-#{$v}-surface);
|
|
255
|
+
border: var(--badge-#{$v}-border-width) solid var(--badge-#{$v}-border);
|
|
256
|
+
border-radius: var(--badge-#{$v}-radius);
|
|
257
|
+
@include themed-padding(--badge-#{$v}-padding, $h: 2);
|
|
258
|
+
font-family: var(--badge-#{$v}-text-font-family);
|
|
259
|
+
font-size: var(--badge-#{$v}-text-font-size);
|
|
260
|
+
font-weight: var(--badge-#{$v}-text-font-weight);
|
|
261
|
+
line-height: var(--badge-#{$v}-text-line-height);
|
|
262
|
+
box-shadow: var(--badge-#{$v}-shadow);
|
|
263
|
+
}
|
|
73
264
|
}
|
|
74
265
|
|
|
75
|
-
/*
|
|
76
|
-
.badge-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
border: 1px solid var(--border-primary-strong);
|
|
80
|
-
text-transform: capitalize;
|
|
266
|
+
/* Flush wins over per-variant radius via source order — declared after @each. */
|
|
267
|
+
.badge-flush {
|
|
268
|
+
--badge-offset: 0;
|
|
269
|
+
border-radius: 0;
|
|
81
270
|
}
|
|
82
271
|
</style>
|