@motion-proto/live-tokens 0.80.0 → 0.82.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/CHANGELOG.md +35 -0
- package/package.json +17 -1
- package/src/demo/Demo.svelte +97 -0
- package/src/demo/Demo.svelte.d.ts +7 -0
- package/src/demo/DemoBar.svelte +75 -0
- package/src/demo/FloatingTokenTags.css +284 -0
- package/src/demo/FloatingTokenTags.svelte +570 -0
- package/src/demo/LabeledSelect.svelte +243 -0
- package/src/demo/Section.svelte +46 -0
- package/src/demo/SketchSelect.svelte +58 -0
- package/src/demo/SourceLinks.svelte +156 -0
- package/src/demo/TestingLoops.svelte +876 -0
- package/src/demo/TestingLoops.svelte.d.ts +4 -0
- package/src/demo/ThemeSelect.svelte +52 -0
- package/src/demo/sections/SectionClaude.svelte +192 -0
- package/src/demo/sections/SectionFooter.svelte +67 -0
- package/src/demo/sections/SectionGetStarted.svelte +100 -0
- package/src/demo/sections/SectionHero.svelte +201 -0
- package/src/demo/sections/SectionKit.svelte +410 -0
- package/src/demo/sections/SectionLayers.svelte +54 -0
- package/src/demo/sections/SectionOffering.svelte +135 -0
- package/src/demo/sketchCarry.ts +91 -0
- package/src/editor/skill-atlas/SkillAtlas.svelte +42 -19
- package/src/editor/skill-atlas/atlasLink.ts +43 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.82.0 — The demo ships from the package
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **The demo page and Testing Loops ship as exports.** Mount
|
|
8
|
+
`@motion-proto/live-tokens/demo` and `@motion-proto/live-tokens/demo/testing-loops`
|
|
9
|
+
as `lazy` routes. The demo takes `onThemePick(fileName)` and
|
|
10
|
+
`onSketchPick(id | null)`, called after the Theme or Sketch select applies a
|
|
11
|
+
pick. Testing Loops takes `homeHref` for its back link, which defaults to
|
|
12
|
+
`/demo`.
|
|
13
|
+
- **The Theme select keeps the sketchstyle on screen.** Picking a theme no
|
|
14
|
+
longer replaces a sketchstyle the visitor picked. The carry runs in every
|
|
15
|
+
document that calls `installSketchCarry()` from
|
|
16
|
+
`@motion-proto/live-tokens/demo/sketch-carry`, and the editor frame needs it
|
|
17
|
+
too, so call it from the app entry.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **The demo's Theme and Sketch selects sit in a bar at the top of the page.**
|
|
22
|
+
The bar stays fixed while the page scrolls, and it shows only where the
|
|
23
|
+
editor runs. They left the "Swap tokens with a click" section. On a phone
|
|
24
|
+
they sit above the hero and scroll with the page.
|
|
25
|
+
- **The Sketch select reads Custom for adjusted dials.** Dials moved off every
|
|
26
|
+
named sketchstyle show the Custom placeholder.
|
|
27
|
+
|
|
28
|
+
## 0.81.0 — Skill Atlas links name a block
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **A Skill Atlas link opens one block.** Clicking a card or badge writes its
|
|
33
|
+
link to the address bar, named after the card title and the badge label:
|
|
34
|
+
`#set-type/write-the-font-pairing/voice`. Opening the link selects that
|
|
35
|
+
block and scrolls both panes to it. `#set-type` still opens the skill, and
|
|
36
|
+
a link to a block that no longer exists opens its skill.
|
|
37
|
+
|
|
3
38
|
## 0.80.0 — The build runs the design checks
|
|
4
39
|
|
|
5
40
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@motion-proto/live-tokens",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.82.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
|
|
6
6
|
"keywords": [
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
"src/editor",
|
|
24
24
|
"src/system",
|
|
25
25
|
"src/app/site.css",
|
|
26
|
+
"src/demo",
|
|
27
|
+
"!src/demo/FloatingTagsPlayground.svelte",
|
|
26
28
|
"src/live-tokens/data/tokens.generated.css",
|
|
27
29
|
"src/live-tokens/data/colors-and-type/default.json",
|
|
28
30
|
"src/live-tokens/data/colors-and-type/autumn.json",
|
|
@@ -108,6 +110,20 @@
|
|
|
108
110
|
"svelte": "./src/editor/skill-atlas/SkillAtlas.svelte",
|
|
109
111
|
"default": "./src/editor/skill-atlas/SkillAtlas.svelte"
|
|
110
112
|
},
|
|
113
|
+
"./demo": {
|
|
114
|
+
"types": "./src/demo/Demo.svelte.d.ts",
|
|
115
|
+
"svelte": "./src/demo/Demo.svelte",
|
|
116
|
+
"default": "./src/demo/Demo.svelte"
|
|
117
|
+
},
|
|
118
|
+
"./demo/testing-loops": {
|
|
119
|
+
"types": "./src/demo/TestingLoops.svelte.d.ts",
|
|
120
|
+
"svelte": "./src/demo/TestingLoops.svelte",
|
|
121
|
+
"default": "./src/demo/TestingLoops.svelte"
|
|
122
|
+
},
|
|
123
|
+
"./demo/sketch-carry": {
|
|
124
|
+
"types": "./src/demo/sketchCarry.ts",
|
|
125
|
+
"default": "./src/demo/sketchCarry.ts"
|
|
126
|
+
},
|
|
111
127
|
"./backdrop": {
|
|
112
128
|
"svelte": "./src/system/backdrop/index.ts",
|
|
113
129
|
"types": "./src/system/backdrop/index.ts",
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import '../app/site.css';
|
|
3
|
+
import SectionHero from './sections/SectionHero.svelte';
|
|
4
|
+
import SectionKit from './sections/SectionKit.svelte';
|
|
5
|
+
import SectionLayers from './sections/SectionLayers.svelte';
|
|
6
|
+
import SectionGetStarted from './sections/SectionGetStarted.svelte';
|
|
7
|
+
import SectionClaude from './sections/SectionClaude.svelte';
|
|
8
|
+
import SectionOffering from './sections/SectionOffering.svelte';
|
|
9
|
+
import SectionFooter from './sections/SectionFooter.svelte';
|
|
10
|
+
import DemoBar from './DemoBar.svelte';
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
onThemePick,
|
|
14
|
+
onSketchPick,
|
|
15
|
+
}: {
|
|
16
|
+
onThemePick?: (fileName: string) => void;
|
|
17
|
+
onSketchPick?: (id: string | null) => void;
|
|
18
|
+
} = $props();
|
|
19
|
+
|
|
20
|
+
// The pickers drive the editor's API, which the package only serves in dev.
|
|
21
|
+
const isDev = import.meta.env.DEV;
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
{#if isDev}
|
|
25
|
+
<DemoBar {onThemePick} {onSketchPick} />
|
|
26
|
+
{/if}
|
|
27
|
+
|
|
28
|
+
<div class="kit" class:has-bar={isDev}>
|
|
29
|
+
<SectionHero />
|
|
30
|
+
<SectionKit />
|
|
31
|
+
<SectionLayers />
|
|
32
|
+
<SectionOffering />
|
|
33
|
+
<SectionGetStarted />
|
|
34
|
+
<SectionClaude />
|
|
35
|
+
<SectionFooter />
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<style>
|
|
39
|
+
.kit {
|
|
40
|
+
display: grid;
|
|
41
|
+
grid-template-columns: repeat(var(--columns-count), 1fr);
|
|
42
|
+
column-gap: var(--columns-gutter);
|
|
43
|
+
row-gap: var(--space-64);
|
|
44
|
+
max-width: var(--columns-max-width);
|
|
45
|
+
margin: 0 auto;
|
|
46
|
+
padding: var(--space-48) var(--space-32);
|
|
47
|
+
min-height: 100vh;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Clears the fixed DemoBar: its padding plus one control's height. */
|
|
51
|
+
.kit.has-bar {
|
|
52
|
+
padding-top: calc(var(--space-48) + var(--space-64));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Per-section gap overrides (added on top of .kit row-gap). */
|
|
56
|
+
.kit > :global(.hero) {
|
|
57
|
+
margin-bottom: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Equal lead-in above these three sections: row-gap (64) + 48 = 112px. */
|
|
61
|
+
.kit > :global(.kit-section),
|
|
62
|
+
.kit > :global(.layers),
|
|
63
|
+
.kit > :global(.claude) {
|
|
64
|
+
margin-top: var(--space-32);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Overlap the offering image up into Layers (top) and butt Get Started
|
|
68
|
+
against its bottom. -space-64 cancels the grid row-gap; the extra
|
|
69
|
+
-space-64 on top is the actual overlap — tune that second term to taste. */
|
|
70
|
+
.kit > :global(.offering) {
|
|
71
|
+
margin-top: calc(var(--space-64) * -2 + var(--space-8));
|
|
72
|
+
margin-bottom: calc(var(--space-64) * -1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Inline code styling cascades into every section via :global descendants. */
|
|
76
|
+
.kit :global(p > code),
|
|
77
|
+
.kit :global(.callout code) {
|
|
78
|
+
background: var(--tint-low);
|
|
79
|
+
padding: var(--space-2) var(--space-6);
|
|
80
|
+
border-radius: var(--radius-sm);
|
|
81
|
+
font-family: var(--font-mono);
|
|
82
|
+
font-size: 0.88em;
|
|
83
|
+
color: var(--text-primary);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@media (max-width: 600px) {
|
|
87
|
+
.kit,
|
|
88
|
+
.kit.has-bar {
|
|
89
|
+
padding: var(--space-32) var(--space-16);
|
|
90
|
+
row-gap: var(--space-48);
|
|
91
|
+
}
|
|
92
|
+
.kit > :global(.offering) {
|
|
93
|
+
margin-top: calc(var(--space-48) * -2);
|
|
94
|
+
margin-bottom: calc(var(--space-48) * -1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
</style>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import ThemeSelect from './ThemeSelect.svelte';
|
|
3
|
+
import SketchSelect from './SketchSelect.svelte';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
onThemePick,
|
|
7
|
+
onSketchPick,
|
|
8
|
+
}: {
|
|
9
|
+
onThemePick?: (fileName: string) => void;
|
|
10
|
+
onSketchPick?: (id: string | null) => void;
|
|
11
|
+
} = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<div class="demo-bar">
|
|
15
|
+
<div class="bar">
|
|
16
|
+
<div class="pickers">
|
|
17
|
+
<ThemeSelect inline size="small" onpick={onThemePick} />
|
|
18
|
+
<SketchSelect inline size="small" onpick={onSketchPick} />
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<style>
|
|
24
|
+
.demo-bar {
|
|
25
|
+
position: fixed;
|
|
26
|
+
top: 0;
|
|
27
|
+
left: 0;
|
|
28
|
+
right: 0;
|
|
29
|
+
z-index: var(--z-sticky);
|
|
30
|
+
background: var(--scrim-high);
|
|
31
|
+
backdrop-filter: blur(var(--blur-md));
|
|
32
|
+
border-bottom: var(--border-width-1) solid var(--tint-high);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* The bar repeats the demo's .kit grid, so its content lines up with the
|
|
36
|
+
sections beneath it. The right side stays empty for the editor's collapsed
|
|
37
|
+
toggle, which pins top-right. */
|
|
38
|
+
.bar {
|
|
39
|
+
max-width: var(--columns-max-width);
|
|
40
|
+
margin-inline: auto;
|
|
41
|
+
padding: var(--space-12) var(--space-32);
|
|
42
|
+
display: grid;
|
|
43
|
+
grid-template-columns: repeat(var(--columns-count), 1fr);
|
|
44
|
+
column-gap: var(--columns-gutter);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.pickers {
|
|
48
|
+
grid-column: 2 / -2;
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-wrap: wrap;
|
|
51
|
+
align-items: center;
|
|
52
|
+
column-gap: var(--space-24);
|
|
53
|
+
row-gap: var(--space-8);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@media (max-width: 960px) {
|
|
57
|
+
.pickers {
|
|
58
|
+
grid-column: 1 / -1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* No room beside the editor's toggle on a phone, so the pickers sit in the
|
|
63
|
+
page below it and scroll away. */
|
|
64
|
+
@media (max-width: 600px) {
|
|
65
|
+
.demo-bar {
|
|
66
|
+
position: static;
|
|
67
|
+
background: none;
|
|
68
|
+
backdrop-filter: none;
|
|
69
|
+
border-bottom: none;
|
|
70
|
+
}
|
|
71
|
+
.bar {
|
|
72
|
+
padding: var(--space-64) var(--space-16) 0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
</style>
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Decoupled from the design system: pills, knots, strings, balls, and
|
|
3
|
+
* animations are hardcoded so token edits don't repaint our chrome. The
|
|
4
|
+
* exceptions are intentional and ARE the demo: the central box's
|
|
5
|
+
* surface/radius/border (set inline), the dropdown (rendered through
|
|
6
|
+
* <MenuSelect>), elevation, which reads from the --shadow-* scale, and the
|
|
7
|
+
* zap colour, which tracks --text-accent so the beam stays on-theme.
|
|
8
|
+
* File is global; keep selectors `ftt-` prefixed.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
.ftt-stage {
|
|
12
|
+
--ftt-contrast-color: var(--color-white);
|
|
13
|
+
--ftt-zap-color: var(--text-accent);
|
|
14
|
+
--ftt-string-opacity: 0.5;
|
|
15
|
+
position: relative;
|
|
16
|
+
width: 100%;
|
|
17
|
+
height: 100%;
|
|
18
|
+
/* No overflow clipping: the bottom tag's dropdown must extend past the
|
|
19
|
+
stage edge. */
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* --- Central box ---------------------------------------------------------- */
|
|
23
|
+
.ftt-box {
|
|
24
|
+
--ftt-box-contrast-color: var(--color-white);
|
|
25
|
+
position: absolute;
|
|
26
|
+
left: 50%;
|
|
27
|
+
top: 50%;
|
|
28
|
+
translate: -50% -50%;
|
|
29
|
+
box-sizing: border-box;
|
|
30
|
+
padding: 0.75rem 1.5rem;
|
|
31
|
+
display: inline-flex;
|
|
32
|
+
width: 12rem;
|
|
33
|
+
align-items: center;
|
|
34
|
+
justify-content: center;
|
|
35
|
+
/* Defaults; overridden inline from the active token bindings. */
|
|
36
|
+
background: color-mix(in srgb, #264542 50%, transparent);
|
|
37
|
+
border-style: solid;
|
|
38
|
+
border-color: #52726e;
|
|
39
|
+
border-width: 2px;
|
|
40
|
+
border-radius: 0.25rem;
|
|
41
|
+
box-shadow: var(--shadow-md);
|
|
42
|
+
transition:
|
|
43
|
+
background 300ms ease,
|
|
44
|
+
border-color 300ms ease,
|
|
45
|
+
border-width 300ms ease,
|
|
46
|
+
border-radius 300ms ease;
|
|
47
|
+
z-index: 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.ftt-box-label {
|
|
51
|
+
font-family: Manrope, system-ui, -apple-system, sans-serif;
|
|
52
|
+
font-size: 1.25rem;
|
|
53
|
+
font-weight: 700;
|
|
54
|
+
color: var(--ftt-box-contrast-color);
|
|
55
|
+
white-space: nowrap;
|
|
56
|
+
user-select: none;
|
|
57
|
+
pointer-events: none;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.ftt-box.ftt-bloop {
|
|
61
|
+
animation: ftt-bloop 480ms ease-out;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@keyframes ftt-bloop {
|
|
65
|
+
0% { scale: 1; filter: brightness(1); }
|
|
66
|
+
30% { scale: 1.08; filter: brightness(1.8) saturate(1.4); }
|
|
67
|
+
100% { scale: 1; filter: brightness(1); }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* --- Kite strings --------------------------------------------------------- */
|
|
71
|
+
.ftt-strings {
|
|
72
|
+
position: absolute;
|
|
73
|
+
inset: 0;
|
|
74
|
+
width: 100%;
|
|
75
|
+
height: 100%;
|
|
76
|
+
pointer-events: none;
|
|
77
|
+
z-index: 2;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.ftt-string {
|
|
81
|
+
stroke: var(--ftt-contrast-color);
|
|
82
|
+
stroke-width: 2;
|
|
83
|
+
stroke-linecap: round;
|
|
84
|
+
vector-effect: non-scaling-stroke;
|
|
85
|
+
opacity: var(--ftt-string-opacity);
|
|
86
|
+
transition: opacity 150ms ease, stroke 150ms ease;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.ftt-string.ftt-live {
|
|
90
|
+
stroke: var(--ftt-zap-color);
|
|
91
|
+
opacity: 1;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* --- Anchor knots --------------------------------------------------------- */
|
|
95
|
+
.ftt-knot {
|
|
96
|
+
position: absolute;
|
|
97
|
+
width: 0.625rem;
|
|
98
|
+
height: 0.625rem;
|
|
99
|
+
translate: -50% -50%;
|
|
100
|
+
border-radius: 9999px;
|
|
101
|
+
background: #c4bcb6;
|
|
102
|
+
border: 1px solid #f0eae4;
|
|
103
|
+
pointer-events: none;
|
|
104
|
+
z-index: 3;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* --- Energy ball ---------------------------------------------------------- */
|
|
108
|
+
/* Shares --ftt-zap-color with `.ftt-string.ftt-live` so the beam reads as a
|
|
109
|
+
continuous trail, not a colour-shifting projectile. */
|
|
110
|
+
.ftt-energy-ball {
|
|
111
|
+
position: absolute;
|
|
112
|
+
width: 0.875rem;
|
|
113
|
+
height: 0.875rem;
|
|
114
|
+
translate: -50% -50%;
|
|
115
|
+
border-radius: 9999px;
|
|
116
|
+
background: var(--ftt-zap-color);
|
|
117
|
+
box-shadow:
|
|
118
|
+
0 0 8px 2px var(--ftt-zap-color),
|
|
119
|
+
0 0 18px 4px var(--ftt-zap-color);
|
|
120
|
+
pointer-events: none;
|
|
121
|
+
opacity: 0;
|
|
122
|
+
z-index: 6;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* --- Floating tag wrapper ------------------------------------------------- */
|
|
126
|
+
.ftt-float {
|
|
127
|
+
position: absolute;
|
|
128
|
+
translate: -50% -50%;
|
|
129
|
+
rotate: var(--ftt-rot, 0deg);
|
|
130
|
+
animation: ftt-bob var(--ftt-bob-duration, 7s) ease-in-out infinite;
|
|
131
|
+
z-index: 4;
|
|
132
|
+
pointer-events: none;
|
|
133
|
+
display: flex;
|
|
134
|
+
flex-direction: column;
|
|
135
|
+
align-items: center;
|
|
136
|
+
gap: 0.25rem;
|
|
137
|
+
width: 14rem;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Lift open tag so its dropdown paints over sibling tags. */
|
|
141
|
+
.ftt-float.ftt-open {
|
|
142
|
+
z-index: 20;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Bottom-placement chips mirror the top arc: chip first, eyebrow on the
|
|
146
|
+
outer (lower) side. Dropdown opens upward toward the box so the menu
|
|
147
|
+
doesn't fall off the stage. */
|
|
148
|
+
.ftt-float[data-placement="bottom"] {
|
|
149
|
+
flex-direction: column-reverse;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Pivot the tilt around the chip's right semicircular cap. The chip is
|
|
153
|
+
~1.84rem tall so the cap center sits ~0.9rem from the chip's right edge
|
|
154
|
+
and ~0.9rem above its bottom — which (since the chip is the widest, last
|
|
155
|
+
in-flow element of the wrapper) coincides with `calc(100% - 0.9rem)` on
|
|
156
|
+
both axes of the wrapper. */
|
|
157
|
+
.ftt-float[data-pivot="right-cap"] {
|
|
158
|
+
transform-origin: calc(100% - 0.9rem) calc(100% - 0.9rem);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@keyframes ftt-bob {
|
|
162
|
+
0%, 100% { translate: -50% -50%; }
|
|
163
|
+
50% { translate: -50% calc(-50% + var(--ftt-bob-distance, -8px)); }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* --- Property label paired with each tag ---------------------------------- */
|
|
167
|
+
/* Sits on the outer side of the chip: above for top-placement chips, below
|
|
168
|
+
for bottom-placement chips (see `.ftt-float[data-placement="bottom"]`). */
|
|
169
|
+
.ftt-float-property {
|
|
170
|
+
font-family: Manrope, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
|
171
|
+
font-size: 0.875rem;
|
|
172
|
+
font-weight: 700;
|
|
173
|
+
color: var(--text-secondary);
|
|
174
|
+
white-space: nowrap;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* --- Tag pill ------------------------------------------------------------- */
|
|
178
|
+
/* Hardcoded (not the Badge component) so badge-* token edits don't repaint. */
|
|
179
|
+
.ftt-tag {
|
|
180
|
+
display: inline-flex;
|
|
181
|
+
align-items: center;
|
|
182
|
+
gap: 0.25rem;
|
|
183
|
+
padding: 0.375rem 0.75rem;
|
|
184
|
+
/* Polished chrome, not brushed steel: the bands keep their spacing but sit
|
|
185
|
+
high in the scale, so the pill stays bright against a cream page. Keep
|
|
186
|
+
the darkest stop ≥ #8f9090 or the black label loses AA. */
|
|
187
|
+
background: linear-gradient(
|
|
188
|
+
135deg,
|
|
189
|
+
#fbfbfb 0%,
|
|
190
|
+
#c9cbcb 38%,
|
|
191
|
+
#eff0f0 62%,
|
|
192
|
+
#b6b8b8 100%
|
|
193
|
+
);
|
|
194
|
+
color: #000;
|
|
195
|
+
border: 1px solid rgba(86, 96, 104, 0.45);
|
|
196
|
+
border-radius: 9999px;
|
|
197
|
+
box-shadow: var(--shadow-md);
|
|
198
|
+
font-family: Manrope, system-ui, -apple-system, sans-serif;
|
|
199
|
+
font-size: 0.875rem;
|
|
200
|
+
font-weight: 600;
|
|
201
|
+
line-height: 1.1;
|
|
202
|
+
white-space: nowrap;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.ftt-tag-icon {
|
|
206
|
+
display: inline-flex;
|
|
207
|
+
align-items: center;
|
|
208
|
+
font-size: 0.875em;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.ftt-tag-label {
|
|
212
|
+
display: inline-block;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* --- Tag trigger (draggable button wrapping the pill) --------------------- */
|
|
216
|
+
.ftt-tag-trigger {
|
|
217
|
+
pointer-events: auto;
|
|
218
|
+
background: transparent;
|
|
219
|
+
border: none;
|
|
220
|
+
padding: 0;
|
|
221
|
+
margin: 0;
|
|
222
|
+
cursor: grab;
|
|
223
|
+
touch-action: none;
|
|
224
|
+
transition: filter 150ms ease, scale 150ms ease;
|
|
225
|
+
}
|
|
226
|
+
.ftt-tag-trigger:hover {
|
|
227
|
+
filter: brightness(1.2);
|
|
228
|
+
scale: 1.04;
|
|
229
|
+
}
|
|
230
|
+
.ftt-float.ftt-dragging .ftt-tag-trigger {
|
|
231
|
+
cursor: grabbing;
|
|
232
|
+
}
|
|
233
|
+
.ftt-float.ftt-dragging {
|
|
234
|
+
animation: none; /* pause bob while dragging */
|
|
235
|
+
z-index: 30;
|
|
236
|
+
}
|
|
237
|
+
.ftt-tag-trigger:focus-visible {
|
|
238
|
+
outline: 2px solid var(--ftt-zap-color);
|
|
239
|
+
outline-offset: 3px;
|
|
240
|
+
border-radius: 9999px;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.ftt-float.ftt-flash .ftt-tag-trigger {
|
|
244
|
+
animation: ftt-tag-flash 500ms ease-out;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
@keyframes ftt-tag-flash {
|
|
248
|
+
0% { scale: 1; filter: brightness(1) drop-shadow(0 0 0 transparent); }
|
|
249
|
+
25% { scale: 1.14; filter: brightness(1.8) drop-shadow(0 0 14px #fff5f0); }
|
|
250
|
+
100% { scale: 1; filter: brightness(1) drop-shadow(0 0 0 transparent); }
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/* --- Chip host ------------------------------------------------------------ */
|
|
254
|
+
/* Wraps the chip-trigger and (when open) the dropdown so the dropdown can
|
|
255
|
+
anchor directly to the chip's edges, not the float wrapper's. Without
|
|
256
|
+
this, `top: 100%` lands at the wrapper bottom — which is the eyebrow for
|
|
257
|
+
`data-placement="bottom"` — and the menu drifts off the chip. */
|
|
258
|
+
.ftt-chip-host {
|
|
259
|
+
position: relative;
|
|
260
|
+
display: inline-flex;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* --- Dropdown menu -------------------------------------------------------- */
|
|
264
|
+
/* The wrap handles position/stacking only; the surface lives on
|
|
265
|
+
<MenuSelect> so menuselect-* token edits reshape the in-flight UI. */
|
|
266
|
+
.ftt-dropdown-wrap {
|
|
267
|
+
position: absolute;
|
|
268
|
+
top: calc(100% + 0.25rem);
|
|
269
|
+
left: 50%;
|
|
270
|
+
translate: -50% 0;
|
|
271
|
+
pointer-events: auto;
|
|
272
|
+
z-index: 10;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* --- Reduced motion ------------------------------------------------------- */
|
|
276
|
+
@media (prefers-reduced-motion: reduce) {
|
|
277
|
+
.ftt-float {
|
|
278
|
+
animation: none;
|
|
279
|
+
}
|
|
280
|
+
.ftt-box.ftt-bloop,
|
|
281
|
+
.ftt-float.ftt-flash .ftt-tag-trigger {
|
|
282
|
+
animation: none;
|
|
283
|
+
}
|
|
284
|
+
}
|