Reverted text-xs override. text-ss replaces the text-xs override while tailwind's existing text-xs replaces our text-2xs.

This commit is contained in:
2026-07-17 12:19:20 -04:00
parent dda0e738d4
commit 4f0418ea6e
43 changed files with 159 additions and 159 deletions

View File

@@ -232,7 +232,7 @@ func badgesSection() func() *VNode {
ui.EnvBadge("staging"), ui.EnvBadge("staging"),
// Production deliberately renders NOTHING — the badge exists to tell you that // Production deliberately renders NOTHING — the badge exists to tell you that
// you are NOT in production, and a badge that is always there says nothing. // you are NOT in production, and a badge that is always there says nothing.
Span(Attr("class", "text-xs text-ink-muted"), Span(Attr("class", "text-ss text-ink-muted"),
Text("(production renders nothing — that is the point of it)")), Text("(production renders nothing — that is the point of it)")),
), ),
), ),
@@ -473,7 +473,7 @@ func selectsSection() func() *VNode {
field("Timezone", ui.FormTimezoneSelector(ui.FormSelectProps{ field("Timezone", ui.FormTimezoneSelector(ui.FormSelectProps{
Value: tz.Get(), OnChange: func(v string) { tz.Set(v) }})), Value: tz.Get(), OnChange: func(v string) { tz.Set(v) }})),
), ),
P(Attr("class", "mt-3 text-xs text-ink-muted"), P(Attr("class", "mt-3 text-ss text-ink-muted"),
Text("one="+orElse(one.Get(), "—")+" several="+orElse(strings.Join(langs.Get(), ","), "—"))), Text("one="+orElse(one.Get(), "—")+" several="+orElse(strings.Join(langs.Get(), ","), "—"))),
), ),
@@ -484,7 +484,7 @@ func selectsSection() func() *VNode {
OnSelect: func(o ui.FormSelectOption) { picked.Set(o.Label + " <" + o.Value + ">") }, OnSelect: func(o ui.FormSelectOption) { picked.Set(o.Label + " <" + o.Value + ">") },
}), }),
), ),
P(Attr("class", "mt-3 text-xs text-ink-muted"), P(Attr("class", "mt-3 text-ss text-ink-muted"),
Text("Two characters before it asks; 200 ms after you stop typing. A response for a query "+ Text("Two characters before it asks; 200 ms after you stop typing. A response for a query "+
"you have already typed past is DISCARDED rather than shown — which is the whole bug "+ "you have already typed past is DISCARDED rather than shown — which is the whole bug "+
"with hand-rolled autocompletes.")), "with hand-rolled autocompletes.")),
@@ -500,7 +500,7 @@ func selectsSection() func() *VNode {
ShowSelectAll: true, ShowSelectAll: true,
OnChange: func(v []string) { tags.Set(v) }, OnChange: func(v []string) { tags.Set(v) },
}), }),
P(Attr("class", "mt-3 text-xs text-ink-muted"), P(Attr("class", "mt-3 text-ss text-ink-muted"),
Text("Same selection model as the field above; only the thing you click on differs.")), Text("Same selection model as the field above; only the thing you click on differs.")),
), ),
) )
@@ -535,7 +535,7 @@ func togglesSection() func() *VNode {
demo("Signature pad — "+itoa(len(signed.Get()))+" bytes of SVG", demo("Signature pad — "+itoa(len(signed.Get()))+" bytes of SVG",
pad.Render(ui.SignaturePadProps{}), pad.Render(ui.SignaturePadProps{}),
P(Attr("class", "mt-3 text-xs text-ink-muted"), P(Attr("class", "mt-3 text-ss text-ink-muted"),
Text("Draw in it. Long strokes are smoothed; a single stray point is not a stroke and "+ Text("Draw in it. Long strokes are smoothed; a single stray point is not a stroke and "+
"is dropped, so a click does not leave a dot behind.")), "is dropped, so a click does not leave a dot behind.")),
), ),
@@ -584,7 +584,7 @@ func datesSection() func() *VNode {
OnNavigate: func(m time.Time) { month.Set(m) }, OnNavigate: func(m time.Time) { month.Set(m) },
}), }),
), ),
P(Attr("class", "mt-3 text-xs text-ink-muted"), P(Attr("class", "mt-3 text-ss text-ink-muted"),
Text("The same grid the picker drops down, usable directly when you want it inline.")), Text("The same grid the picker drops down, usable directly when you want it inline.")),
), ),
) )
@@ -676,7 +676,7 @@ func tablesSection() func() *VNode {
SortDesc: gridDesc.Get(), SortDesc: gridDesc.Get(),
SetSortDesc: func(b bool) { gridDesc.Set(b) }, SetSortDesc: func(b bool) { gridDesc.Set(b) },
}), }),
P(Attr("class", "mt-3 text-xs text-ink-muted"), P(Attr("class", "mt-3 text-ss text-ink-muted"),
Text("Click a Qty or Price cell and type. The grid does not own the rows — it tells you "+ Text("Click a Qty or Price cell and type. The grid does not own the rows — it tells you "+
"which cell changed and hands the value back; what you do with it is yours.")), "which cell changed and hands the value back; what you do with it is yours.")),
), ),
@@ -801,7 +801,7 @@ func overlaysSection(push func(ui.ToastType, string)) func() *VNode {
tipFocus.Render(Span(Text("Shown on FOCUS, not hover — tab to the field")), tipFocus.Render(Span(Text("Shown on FOCUS, not hover — tab to the field")),
ui.FormInput(ui.FormInputProps{Placeholder: "Focus me"})), ui.FormInput(ui.FormInputProps{Placeholder: "Focus me"})),
), ),
P(Attr("class", "mt-3 text-xs text-ink-muted"), P(Attr("class", "mt-3 text-ss text-ink-muted"),
Text("Narrow the window and hover \"Right\": it flips to the left, and its arrow follows "+ Text("Narrow the window and hover \"Right\": it flips to the left, and its arrow follows "+
"it. A tooltip that only answers to a mouse is a tooltip a keyboard user cannot read.")), "it. A tooltip that only answers to a mouse is a tooltip a keyboard user cannot read.")),
), ),
@@ -862,7 +862,7 @@ func overlaysSection(push func(ui.ToastType, string)) func() *VNode {
hoverMenu.Item(ui.MenuItemProps{}, Text("Two")), hoverMenu.Item(ui.MenuItemProps{}, Text("Two")),
), ),
), ),
P(Attr("class", "mt-3 text-xs text-ink-muted"), P(Attr("class", "mt-3 text-ss text-ink-muted"),
Text("Opening one closes the other — a single-open manager. Submenus are exempt, or a "+ Text("Opening one closes the other — a single-open manager. Submenus are exempt, or a "+
"submenu would close the very menu it belongs to as it opened. The items raise toasts, "+ "submenu would close the very menu it belongs to as it opened. The items raise toasts, "+
"which is how you can see that an item really does close its own menu.")), "which is how you can see that an item really does close its own menu.")),
@@ -889,7 +889,7 @@ func overlaysSection(push func(ui.ToastType, string)) func() *VNode {
}, ui.ModalOptions{Size: ui.ModalSmall}) }, ui.ModalOptions{Size: ui.ModalSmall})
}}), }}),
), ),
P(Attr("class", "mt-3 text-xs text-ink-muted"), P(Attr("class", "mt-3 text-ss text-ink-muted"),
Text("Open the modal, then the nested one inside it, and press Escape twice: modals unwind "+ Text("Open the modal, then the nested one inside it, and press Escape twice: modals unwind "+
"ONE LAYER per press rather than all at once.")), "ONE LAYER per press rather than all at once.")),
@@ -994,7 +994,7 @@ func feedbackSection(toaster *ui.Toaster, tour *ui.Tutorial) func() *VNode {
demo("The guided tour", demo("The guided tour",
row("flex flex-wrap items-center gap-3", row("flex flex-wrap items-center gap-3",
tour.StartButton(0, "", Text("Take the tour")), tour.StartButton(0, "", Text("Take the tour")),
Span(Attr("class", "text-xs text-ink-muted"), Span(Attr("class", "text-ss text-ink-muted"),
Text("It dims the page, cuts a hole around each target, and animates the spotlight from "+ Text("It dims the page, cuts a hole around each target, and animates the spotlight from "+
"one to the next. Targets are CSS SELECTORS — the same section ids the sidebar jumps to.")), "one to the next. Targets are CSS SELECTORS — the same section ids the sidebar jumps to.")),
), ),
@@ -1006,7 +1006,7 @@ func feedbackSection(toaster *ui.Toaster, tour *ui.Tutorial) func() *VNode {
OnClick: flash.Fire}), OnClick: flash.Fire}),
ui.RemoteUpdateFlash(flash.Visible()), ui.RemoteUpdateFlash(flash.Visible()),
), ),
P(Attr("class", "mt-3 text-xs text-ink-muted"), P(Attr("class", "mt-3 text-ss text-ink-muted"),
Text("A two-second acknowledgement that data you are looking at was changed by somebody "+ Text("A two-second acknowledgement that data you are looking at was changed by somebody "+
"else. It is not a toast: it belongs next to the thing that moved, not in the corner.")), "else. It is not a toast: it belongs next to the thing that moved, not in the corner.")),
), ),
@@ -1071,7 +1071,7 @@ func navigationSection() func() *VNode {
demo("Accordion — one open at a time, or several", demo("Accordion — one open at a time, or several",
row("grid gap-6 lg:grid-cols-2", row("grid gap-6 lg:grid-cols-2",
row("flex flex-col gap-2", row("flex flex-col gap-2",
P(Attr("class", "text-xs font-semibold uppercase tracking-widest text-ink-faint"), P(Attr("class", "text-ss font-semibold uppercase tracking-widest text-ink-faint"),
Text("SingleAccordion")), Text("SingleAccordion")),
ui.SingleAccordion([]ui.AccordionItemData{ ui.SingleAccordion([]ui.AccordionItemData{
{Title: "What is Kjøl Wasm Web?", Content: panel("kjol's Go→WebAssembly UI engine.")}, {Title: "What is Kjøl Wasm Web?", Content: panel("kjol's Go→WebAssembly UI engine.")},
@@ -1080,7 +1080,7 @@ func navigationSection() func() *VNode {
}, acc.Get(), func(i int) { acc.Set(i) }), }, acc.Get(), func(i int) { acc.Set(i) }),
), ),
row("flex flex-col gap-2", row("flex flex-col gap-2",
P(Attr("class", "text-xs font-semibold uppercase tracking-widest text-ink-faint"), P(Attr("class", "text-ss font-semibold uppercase tracking-widest text-ink-faint"),
Text("Accordion (several at once)")), Text("Accordion (several at once)")),
ui.Accordion([]ui.AccordionItemData{ ui.Accordion([]ui.AccordionItemData{
{Title: "First", Content: panel("Open me.")}, {Title: "First", Content: panel("Open me.")},
@@ -1107,7 +1107,7 @@ func navigationSection() func() *VNode {
}}, }},
}, func(id string) { side.Set(id) }, ""), }, func(id string) { side.Set(id) }, ""),
), ),
P(Attr("class", "mt-3 text-xs text-ink-muted"), P(Attr("class", "mt-3 text-ss text-ink-muted"),
Text("It reports the id you clicked and nothing else. The sidebar on THIS page is the "+ Text("It reports the id you clicked and nothing else. The sidebar on THIS page is the "+
"same idea, wired to scroll the section into view.")), "same idea, wired to scroll the section into view.")),
), ),
@@ -1230,7 +1230,7 @@ func chartsSection() func() *VNode {
row("mt-4 flex items-center gap-3", row("mt-4 flex items-center gap-3",
ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Small: true, Text: "New data", OnClick: func() { seed.Set(seed.Get() + 1) }}), ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Small: true, Text: "New data", OnClick: func() { seed.Set(seed.Get() + 1) }}),
ui.Button(ui.ButtonProps{Color: ui.ButtonNeutral, Small: true, Text: threeDLabel, OnClick: func() { threeD.Set(!threeD.Get()) }}), ui.Button(ui.ButtonProps{Color: ui.ButtonNeutral, Small: true, Text: threeDLabel, OnClick: func() { threeD.Set(!threeD.Get()) }}),
Span(Attr("class", "text-xs text-ink-muted"), Text("Each chart takes a Title; a multi-series chart also draws a legend. Click a legend key (Errors, or a donut slice) to hide it — the scale and marks recompute. 3D extrudes the bars and tilts the donut.")), Span(Attr("class", "text-ss text-ink-muted"), Text("Each chart takes a Title; a multi-series chart also draws a legend. Click a legend key (Errors, or a donut slice) to hide it — the scale and marks recompute. 3D extrudes the bars and tilts the donut.")),
), ),
), ),
@@ -1247,7 +1247,7 @@ func chartsSection() func() *VNode {
demo("US heatmap — a value per state, with proportional lat/lng points on top", demo("US heatmap — a value per state, with proportional lat/lng points on top",
heat.Render(ui.USHeatmapProps{Data: usSignups, Points: usCities, Proportional: true}), heat.Render(ui.USHeatmapProps{Data: usSignups, Points: usCities, Proportional: true}),
Span(Attr("class", "mt-3 block text-xs text-ink-muted"), Span(Attr("class", "mt-3 block text-ss text-ink-muted"),
Text("webui.USHeatmap shades each state on the choropleth ramp and projects lat/lng points "+ Text("webui.USHeatmap shades each state on the choropleth ramp and projects lat/lng points "+
"with a Go albersUsa port — Anchorage and Honolulu land on the insets. Hover a state or a point.")), "with a Go albersUsa port — Anchorage and Honolulu land on the insets. Hover a state or a point.")),
), ),

View File

@@ -5,6 +5,7 @@ import (
"kjol/lexer" // syntax highlighting for the code blocks — a string in, HTML out "kjol/lexer" // syntax highlighting for the code blocks — a string in, HTML out
. "kjol/vdom" . "kjol/vdom"
// The host API is dual-build — real under js/wasm, no-ops natively — so neutral page // The host API is dual-build — real under js/wasm, no-ops natively — so neutral page
// code can measure the browser and still server-render. This page uses it for exactly // code can measure the browser and still server-render. This page uses it for exactly
// one thing: reading the clock when hydration commits. // one thing: reading the clock when hydration commits.
@@ -89,7 +90,7 @@ func docPage(eyebrow, title, lede string, sections ...*VNode) *VNode {
mods := []Mod{Attr("class", "pb-16")} mods := []Mod{Attr("class", "pb-16")}
mods = append(mods, mods = append(mods,
Div(Attr("class", "border-b border-line pb-6"), Div(Attr("class", "border-b border-line pb-6"),
P(Attr("class", "text-xs font-semibold uppercase tracking-widest text-accent"), Text(eyebrow)), P(Attr("class", "text-ss font-semibold uppercase tracking-widest text-accent"), Text(eyebrow)),
H1(Attr("class", "mt-2 text-3xl font-semibold tracking-tight text-text-heading"), Text(title)), H1(Attr("class", "mt-2 text-3xl font-semibold tracking-tight text-text-heading"), Text(title)),
P(Attr("class", "mt-3 text-ink-muted leading-relaxed"), Text(lede)), P(Attr("class", "mt-3 text-ink-muted leading-relaxed"), Text(lede)),
), ),
@@ -150,7 +151,7 @@ func codeLang(caption, lang, src string) *VNode {
return Div(Attr("class", "mt-4 overflow-hidden rounded-default border border-neutral-800 bg-neutral-900"), return Div(Attr("class", "mt-4 overflow-hidden rounded-default border border-neutral-800 bg-neutral-900"),
Div(Attr("class", "flex items-center gap-2 border-b border-neutral-800 px-4 py-2"), Div(Attr("class", "flex items-center gap-2 border-b border-neutral-800 px-4 py-2"),
Span(Attr("class", "text-xs font-medium text-ink-faint font-mono"), Text(caption)), Span(Attr("class", "text-ss font-medium text-ink-faint font-mono"), Text(caption)),
Span(Attr("class", "ml-auto rounded-full bg-white/5 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-ink-faint"), Text(lang)), Span(Attr("class", "ml-auto rounded-full bg-white/5 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-ink-faint"), Text(lang)),
), ),
Pre(Attr("class", "overflow-x-auto px-4 py-3 text-[13px] leading-relaxed text-neutral-100 font-mono"), body), Pre(Attr("class", "overflow-x-auto px-4 py-3 text-[13px] leading-relaxed text-neutral-100 font-mono"), body),
@@ -164,7 +165,7 @@ func demo(title string, body ...*VNode) *VNode {
mods := []Mod{Attr("class", "mt-4 rounded-default border border-line bg-surface shadow-xs")} mods := []Mod{Attr("class", "mt-4 rounded-default border border-line bg-surface shadow-xs")}
mods = append(mods, mods = append(mods,
Div(Attr("class", "border-b border-line px-4 py-2"), Div(Attr("class", "border-b border-line px-4 py-2"),
Span(Attr("class", "text-xs text-ink-muted"), Text(title)), Span(Attr("class", "text-ss text-ink-muted"), Text(title)),
), ),
) )
inner := []Mod{Attr("class", "p-4")} inner := []Mod{Attr("class", "p-4")}

View File

@@ -247,7 +247,7 @@ func layerItem(d Deps, l Layer) *VNode {
Span(Attr("class", "rounded-full bg-surface-raised px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-ink-muted"), Span(Attr("class", "rounded-full bg-surface-raised px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-ink-muted"),
Text("reference")), Text("reference")),
), ),
Span(Attr("class", "text-xs text-ink-muted"), Text(l.Tagline)), Span(Attr("class", "text-ss text-ink-muted"), Text(l.Tagline)),
) )
} }
@@ -257,6 +257,6 @@ func layerItem(d Deps, l Layer) *VNode {
} }
return A(Attr("class", cls), Attr("href", l.Href), return A(Attr("class", cls), Attr("href", l.Href),
Span(Attr("class", "text-sm font-medium text-ink"), Text(l.Name)), Span(Attr("class", "text-sm font-medium text-ink"), Text(l.Name)),
Span(Attr("class", "text-xs text-ink-muted"), Text(l.Tagline)), Span(Attr("class", "text-ss text-ink-muted"), Text(l.Tagline)),
) )
} }

View File

@@ -410,7 +410,7 @@ serving "./wwwroot" on http://localhost:8085`
func AboutPage(d Deps) func() *VNode { func AboutPage(d Deps) func() *VNode {
return func() *VNode { return func() *VNode {
return Div(Attr("class", "mx-auto max-w-3xl py-4"), return Div(Attr("class", "mx-auto max-w-3xl py-4"),
P(Attr("class", "text-xs font-semibold uppercase tracking-widest text-accent"), Text("About")), P(Attr("class", "text-ss font-semibold uppercase tracking-widest text-accent"), Text("About")),
H1(Attr("class", "mt-2 text-4xl font-semibold tracking-tight text-text-heading"), Text("Why this exists")), H1(Attr("class", "mt-2 text-4xl font-semibold tracking-tight text-text-heading"), Text("Why this exists")),
P(Attr("class", "mt-6 text-lg leading-relaxed text-ink-soft"), P(Attr("class", "mt-6 text-lg leading-relaxed text-ink-soft"),

View File

@@ -98,7 +98,7 @@ function LayerItem(props: { layer: Layer; current?: Layer }) {
reference reference
</span> </span>
</span> </span>
<span class="text-xs text-ink-muted">{props.layer.tagline}</span> <span class="text-ss text-ink-muted">{props.layer.tagline}</span>
</div> </div>
} }
> >
@@ -113,7 +113,7 @@ function LayerItem(props: { layer: Layer; current?: Layer }) {
} }
> >
<span class="text-sm font-medium text-ink">{props.layer.name}</span> <span class="text-sm font-medium text-ink">{props.layer.name}</span>
<span class="text-xs text-ink-muted">{props.layer.tagline}</span> <span class="text-ss text-ink-muted">{props.layer.tagline}</span>
</a> </a>
</Show> </Show>
); );

View File

@@ -112,7 +112,7 @@ function Panel(props: { title: string; children?: JSXElement }) {
return ( return (
<div class="mt-4 rounded-default border border-line bg-surface shadow-xs"> <div class="mt-4 rounded-default border border-line bg-surface shadow-xs">
<div class="border-b border-line px-4 py-2"> <div class="border-b border-line px-4 py-2">
<span class="text-xs text-ink-muted">{props.title}</span> <span class="text-ss text-ink-muted">{props.title}</span>
</div> </div>
<div class="p-4">{props.children}</div> <div class="p-4">{props.children}</div>
</div> </div>
@@ -153,7 +153,7 @@ function Body() {
return ( return (
<div> <div>
<div class="border-b border-line pb-6"> <div class="border-b border-line pb-6">
<p class="text-xs font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p> <p class="text-ss font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p>
<h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink">Components</h1> <h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink">Components</h1>
<p class="mt-3 leading-relaxed text-ink-muted"> <p class="mt-3 leading-relaxed text-ink-muted">
Every component in the Solid kit, running. Not a screenshot of one anywhere: each block Every component in the Solid kit, running. Not a screenshot of one anywhere: each block
@@ -313,7 +313,7 @@ function Badges() {
<span class="text-sm font-semibold text-ink">kjol</span> <span class="text-sm font-semibold text-ink">kjol</span>
<EnvBadge /> <EnvBadge />
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
Nothing beside the wordmark? Then this build is production which is what it is telling Nothing beside the wordmark? Then this build is production which is what it is telling
you. you.
</p> </p>
@@ -429,7 +429,7 @@ function Icons() {
<Icon icon="star" size={18} solid /> <Icon icon="star" size={18} solid />
<Icon icon="star" size={18} solid={false} /> <Icon icon="star" size={18} solid={false} />
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
The last two are the same name in the solid and regular styles. Which one you get by The last two are the same name in the solid and regular styles. Which one you get by
default is a CSS variable the app sets, read at runtime. default is a CSS variable the app sets, read at runtime.
</p> </p>
@@ -614,7 +614,7 @@ function Selects() {
<FormTimezoneSelector value={tz()} onchange={(e: any) => setTz(e?.currentTarget?.value ?? e)} /> <FormTimezoneSelector value={tz()} onchange={(e: any) => setTz(e?.currentTarget?.value ?? e)} />
</Field> </Field>
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
one: <span class="font-mono text-ink">{one() || "—"}</span> · several:{" "} one: <span class="font-mono text-ink">{one() || "—"}</span> · several:{" "}
<span class="font-mono text-ink">{many().join(", ") || "—"}</span> <span class="font-mono text-ink">{many().join(", ") || "—"}</span>
</p> </p>
@@ -635,7 +635,7 @@ function Selects() {
onSelect={(value, option) => setPicked(option.label + " <" + value + ">")} onSelect={(value, option) => setPicked(option.label + " <" + value + ">")}
/> />
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
Two characters before it asks; 200 ms after you stop typing. A response for a query you Two characters before it asks; 200 ms after you stop typing. A response for a query you
have already typed past is DISCARDED rather than shown which is the whole bug with have already typed past is DISCARDED rather than shown which is the whole bug with
hand-rolled autocompletes. hand-rolled autocompletes.
@@ -655,7 +655,7 @@ function Selects() {
searchable searchable
showSelectAll showSelectAll
/> />
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
Same selection model as the field above; only the thing you click on differs. Same selection model as the field above; only the thing you click on differs.
</p> </p>
</Panel> </Panel>
@@ -698,7 +698,7 @@ function Toggles() {
<Panel title={"Signature pad — " + signed().length + " bytes of SVG"}> <Panel title={"Signature pad — " + signed().length + " bytes of SVG"}>
<FormSignaturePad onchange={setSigned} /> <FormSignaturePad onchange={setSigned} />
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
Draw in it. Clearing it emits an empty string, so "did they sign?" is just a length check. Draw in it. Clearing it emits an empty string, so "did they sign?" is just a length check.
</p> </p>
</Panel> </Panel>
@@ -735,7 +735,7 @@ function Dates() {
<div class="max-w-xs"> <div class="max-w-xs">
<Calendar selected={day()} onSelect={setDay} /> <Calendar selected={day()} onSelect={setDay} />
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
The same grid the picker drops down, usable directly when you want it inline. Pass{" "} The same grid the picker drops down, usable directly when you want it inline. Pass{" "}
<code class="font-mono">variant="month"</code> for the big version. <code class="font-mono">variant="month"</code> for the big version.
</p> </p>
@@ -859,7 +859,7 @@ function Tables() {
sortDesc={sortDesc()} sortDesc={sortDesc()}
setSortDesc={setSortDesc} setSortDesc={setSortDesc}
/> />
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
Click a Qty or Price cell and type; Tab and the arrow keys move between editable cells. Click a Qty or Price cell and type; Tab and the arrow keys move between editable cells.
The grid does not own the rows it tells you which cell changed and hands the value back. The grid does not own the rows it tells you which cell changed and hands the value back.
</p> </p>
@@ -972,7 +972,7 @@ function Overlays() {
<FormInput placeholder="Focus me" /> <FormInput placeholder="Focus me" />
</Tooltip> </Tooltip>
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
A tooltip that only answers to a mouse is a tooltip a keyboard user cannot read. A tooltip that only answers to a mouse is a tooltip a keyboard user cannot read.
</p> </p>
</Panel> </Panel>
@@ -1048,7 +1048,7 @@ function Overlays() {
</MenuContent> </MenuContent>
</Menu> </Menu>
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
The items raise toasts, which is how you can see that an item really does close its own The items raise toasts, which is how you can see that an item really does close its own
menu — and that the one marked closeOnClick={"{false}"} does not. menu — and that the one marked closeOnClick={"{false}"} does not.
</p> </p>
@@ -1064,7 +1064,7 @@ function Overlays() {
Open wizard Open wizard
</ButtonUI> </ButtonUI>
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
Open the modal, then the nested one inside it, and press Escape twice: modals unwind ONE Open the modal, then the nested one inside it, and press Escape twice: modals unwind ONE
LAYER per press rather than all at once. LAYER per press rather than all at once.
</p> </p>
@@ -1190,7 +1190,7 @@ function Feedback() {
Sticky (no timer) Sticky (no timer)
</ButtonUI> </ButtonUI>
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
<code class="font-mono">useToast()</code> throws outside a{" "} <code class="font-mono">useToast()</code> throws outside a{" "}
<code class="font-mono">&lt;ToastProvider&gt;</code> which is one of only two providers <code class="font-mono">&lt;ToastProvider&gt;</code> which is one of only two providers
this kit has. this kit has.
@@ -1200,7 +1200,7 @@ function Feedback() {
<Panel title="The guided tour"> <Panel title="The guided tour">
<div class="flex flex-wrap items-center gap-3"> <div class="flex flex-wrap items-center gap-3">
<StartTutorialButton>Take the tour</StartTutorialButton> <StartTutorialButton>Take the tour</StartTutorialButton>
<span class="text-xs text-ink-muted"> <span class="text-ss text-ink-muted">
It dims the page, cuts a hole around each target, and animates the spotlight from one It dims the page, cuts a hole around each target, and animates the spotlight from one
to the next. Targets are CSS SELECTORS the same section ids the sidebar jumps to. to the next. Targets are CSS SELECTORS the same section ids the sidebar jumps to.
</span> </span>
@@ -1214,7 +1214,7 @@ function Feedback() {
</ButtonUI> </ButtonUI>
<RemoteUpdateFlash when={flash.visible()} /> <RemoteUpdateFlash when={flash.visible()} />
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
A brief acknowledgement that data you are looking at was changed by somebody else. It is A brief acknowledgement that data you are looking at was changed by somebody else. It is
not a toast: it belongs next to the thing that moved, not in the corner. not a toast: it belongs next to the thing that moved, not in the corner.
</p> </p>
@@ -1269,7 +1269,7 @@ function Navigation() {
<Panel title="Accordion — one open at a time, or several"> <Panel title="Accordion — one open at a time, or several">
<div class="grid gap-6 lg:grid-cols-2"> <div class="grid gap-6 lg:grid-cols-2">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<p class="text-xs font-semibold uppercase tracking-widest text-ink-faint"> <p class="text-ss font-semibold uppercase tracking-widest text-ink-faint">
SingleAccordion SingleAccordion
</p> </p>
<SingleAccordion <SingleAccordion
@@ -1281,7 +1281,7 @@ function Navigation() {
/> />
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<p class="text-xs font-semibold uppercase tracking-widest text-ink-faint"> <p class="text-ss font-semibold uppercase tracking-widest text-ink-faint">
Accordion (several at once) Accordion (several at once)
</p> </p>
<Accordion <Accordion
@@ -1314,7 +1314,7 @@ function Navigation() {
onItemClick={setSide} onItemClick={setSide}
/> />
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
It scrolls the element whose <code class="font-mono">id</code> matches the item into view It scrolls the element whose <code class="font-mono">id</code> matches the item into view
so these really do jump, because those sections really do exist on this page. so these really do jump, because those sections really do exist on this page.
</p> </p>
@@ -1353,7 +1353,7 @@ function Search() {
onSelect={(value) => setHit(value)} onSelect={(value) => setHit(value)}
/> />
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
The scorer is headless too: <code class="font-mono">rankFuzzyMatches</code> and{" "} The scorer is headless too: <code class="font-mono">rankFuzzyMatches</code> and{" "}
<code class="font-mono">fuzzySegments</code> give you the ranking and the highlight runs, <code class="font-mono">fuzzySegments</code> give you the ranking and the highlight runs,
and you render them however you like. and you render them however you like.
@@ -1438,7 +1438,7 @@ function Charts() {
<ButtonUI color={BUTTON_COLOR_NEUTRAL} small onclick={() => setThreeD(!threeD())}> <ButtonUI color={BUTTON_COLOR_NEUTRAL} small onclick={() => setThreeD(!threeD())}>
{threeD() ? "Flat" : "3D"} {threeD() ? "Flat" : "3D"}
</ButtonUI> </ButtonUI>
<span class="text-xs text-ink-muted"> <span class="text-ss text-ink-muted">
Each chart carries a <code class="font-mono">title</code>; a multi-series chart also draws a Each chart carries a <code class="font-mono">title</code>; a multi-series chart also draws a
legend. Click a legend key say <em>Errors</em> or a donut slice to hide it, and the scale legend. Click a legend key say <em>Errors</em> or a donut slice to hide it, and the scale
and marks recompute from what's left. The <code class="font-mono">threeD</code> prop extrudes and marks recompute from what's left. The <code class="font-mono">threeD</code> prop extrudes
@@ -1468,7 +1468,7 @@ function Charts() {
{ name: "Retries", data: shuffle([5, 7, 3, 9]) }, { name: "Retries", data: shuffle([5, 7, 3, 9]) },
]} height={260} /> ]} height={260} />
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
<code class="font-mono">horizontal</code> runs the categories down the y-axis;{" "} <code class="font-mono">horizontal</code> runs the categories down the y-axis;{" "}
<code class="font-mono">stacked</code> layers the series with a 2px surface gap between segments. <code class="font-mono">stacked</code> layers the series with a 2px surface gap between segments.
</p> </p>
@@ -1476,7 +1476,7 @@ function Charts() {
<Panel title="US heatmap — a value per state, with proportional lat/lng points on top"> <Panel title="US heatmap — a value per state, with proportional lat/lng points on top">
<USHeatmap data={US_SIGNUPS} points={US_CITIES} proportional valueFormat={(v) => v.toLocaleString("en-US")} /> <USHeatmap data={US_SIGNUPS} points={US_CITIES} proportional valueFormat={(v) => v.toLocaleString("en-US")} />
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
<code class="font-mono">@ui/USHeatmap</code> shades each state on a themed sequential ramp and <code class="font-mono">@ui/USHeatmap</code> shades each state on a themed sequential ramp and
projects <code class="font-mono">points</code> (latitude/longitude) with a dependency-free projects <code class="font-mono">points</code> (latitude/longitude) with a dependency-free
albersUsa port — so Anchorage and Honolulu land on the Alaska and Hawaii insets. With{" "} albersUsa port — so Anchorage and Honolulu land on the Alaska and Hawaii insets. With{" "}
@@ -1557,7 +1557,7 @@ function Theming() {
)} )}
</For> </For>
</div> </div>
<p class="mt-3 text-xs text-ink-muted"> <p class="mt-3 text-ss text-ink-muted">
The swatch class is written out in full in the source, not built as{" "} The swatch class is written out in full in the source, not built as{" "}
<code class="font-mono">"bg-" + name</code>. Tailwind finds the classes it must compile by <code class="font-mono">"bg-" + name</code>. Tailwind finds the classes it must compile by
scanning the source for literal strings a concatenation is invisible to it, and every scanning the source for literal strings a concatenation is invisible to it, and every

View File

@@ -13,7 +13,7 @@ export function Overview() {
return ( return (
<div> <div>
<p class="text-xs font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p> <p class="text-ss font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p>
<h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink"> <h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink">
A Solid kit, built by a Go toolchain A Solid kit, built by a Go toolchain
</h1> </h1>
@@ -103,7 +103,7 @@ export function Overview() {
function Stage(props: { n: string; title: string; body: string }) { function Stage(props: { n: string; title: string; body: string }) {
return ( return (
<div class="flex gap-4 border-l-2 border-line pl-4"> <div class="flex gap-4 border-l-2 border-line pl-4">
<span class="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-surface-raised text-xs font-semibold text-ink-soft"> <span class="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-surface-raised text-ss font-semibold text-ink-soft">
{props.n} {props.n}
</span> </span>
<div> <div>

View File

@@ -29,7 +29,7 @@ export function Ssr() {
return ( return (
<div class="page-ssr mx-auto max-w-2xl px-4 py-14"> <div class="page-ssr mx-auto max-w-2xl px-4 py-14">
<p class="text-xs font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p> <p class="text-ss font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p>
<h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink"> <h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink">
This page was rendered by Go This page was rendered by Go
</h1> </h1>

View File

@@ -39,8 +39,7 @@
@theme { @theme {
/* Custom text sizes */ /* Custom text sizes */
--text-2xs: 0.75rem; /* Same size as Tailwind's default text-xs */ --text-ss: 0.8rem; /* Override: Tailwind default is 0.75rem */
--text-xs: 0.8rem; /* Override: Tailwind default is 0.75rem */
/* Custom breakpoints (Tailwind defaults + xs + ultrawide) */ /* Custom breakpoints (Tailwind defaults + xs + ultrawide) */
--breakpoint-xs: 375px; --breakpoint-xs: 375px;

View File

@@ -560,14 +560,14 @@ const HEADER_SORT_ICON_CLS: Record<AutoTableHeaderColor, string> = {
export const HEADER_PADDING_CLS: Record<AutoTableSize, string> = { export const HEADER_PADDING_CLS: Record<AutoTableSize, string> = {
[AUTOTABLE_SIZE_DEFAULT]: "p-4 text-sm", [AUTOTABLE_SIZE_DEFAULT]: "p-4 text-sm",
[AUTOTABLE_SIZE_COMPACT]: "py-1.5 px-2 text-sm", [AUTOTABLE_SIZE_COMPACT]: "py-1.5 px-2 text-sm",
[AUTOTABLE_SIZE_SUPERCOMPACT]: "py-1 px-2 text-xs", [AUTOTABLE_SIZE_SUPERCOMPACT]: "py-1 px-2 text-ss",
}; };
// Body cell padding per table size — applied via [&_td]: on the tbody. // Body cell padding per table size — applied via [&_td]: on the tbody.
export const BODY_PADDING_CLS: Record<AutoTableSize, string> = { export const BODY_PADDING_CLS: Record<AutoTableSize, string> = {
[AUTOTABLE_SIZE_DEFAULT]: "text-sm [&_td]:p-4", [AUTOTABLE_SIZE_DEFAULT]: "text-sm [&_td]:p-4",
[AUTOTABLE_SIZE_COMPACT]: "text-sm [&_td]:py-1 [&_td]:px-2", [AUTOTABLE_SIZE_COMPACT]: "text-sm [&_td]:py-1 [&_td]:px-2",
[AUTOTABLE_SIZE_SUPERCOMPACT]: "text-xs [&_td]:py-0.5 [&_td]:px-2", [AUTOTABLE_SIZE_SUPERCOMPACT]: "text-ss [&_td]:py-0.5 [&_td]:px-2",
}; };
// Pagination padding per table size. // Pagination padding per table size.
@@ -651,7 +651,7 @@ const FORMULA_PLACEHOLDER = `<span class="text-ink-faint">e.g. [Revenue] / SUM({
// Mobile: filters collapse behind a toggle; lg+ always shows the filter row inline. // Mobile: filters collapse behind a toggle; lg+ always shows the filter row inline.
const FILTERS_TOGGLE = "lg:hidden inline-flex w-full items-center justify-between gap-2 py-1 px-3 text-sm font-normal text-ink bg-surface-muted border border-line-strong rounded-default shadow-xs cursor-pointer hover:bg-surface-raised"; const FILTERS_TOGGLE = "lg:hidden inline-flex w-full items-center justify-between gap-2 py-1 px-3 text-sm font-normal text-ink bg-surface-muted border border-line-strong rounded-default shadow-xs cursor-pointer hover:bg-surface-raised";
const FILTERS_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1.5 text-xs font-semibold text-white bg-sky-700 rounded-full"; const FILTERS_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1.5 text-ss font-semibold text-white bg-sky-700 rounded-full";
const FILTERS_BODY_BASE = "grow flex flex-col gap-3 min-w-0 max-w-full items-stretch overflow-visible lg:items-end max-lg:[&>*]:w-full max-lg:[&>*]:min-w-0 max-lg:[&_.search-fields]:w-full max-lg:[&_.search-fields]:flex-col [&>.quick-date-tags]:w-full"; const FILTERS_BODY_BASE = "grow flex flex-col gap-3 min-w-0 max-w-full items-stretch overflow-visible lg:items-end max-lg:[&>*]:w-full max-lg:[&>*]:min-w-0 max-lg:[&_.search-fields]:w-full max-lg:[&_.search-fields]:flex-col [&>.quick-date-tags]:w-full";
const FILTERS_BODY_INLINE = FILTERS_BODY_BASE const FILTERS_BODY_INLINE = FILTERS_BODY_BASE
+ " lg:flex lg:flex-row lg:flex-wrap lg:items-end lg:gap-x-3.5 lg:gap-y-2.5" + " lg:flex lg:flex-row lg:flex-wrap lg:items-end lg:gap-x-3.5 lg:gap-y-2.5"
@@ -668,14 +668,14 @@ export const SEARCH_FIELDS = "search-fields flex flex-col items-stretch gap-3 w-
export const SEARCH_FIELDS_SPACER = "search-fields-spacer hidden lg:block flex-1 min-w-4 basis-full lg:basis-auto lg:min-w-[1rem]"; export const SEARCH_FIELDS_SPACER = "search-fields-spacer hidden lg:block flex-1 min-w-4 basis-full lg:basis-auto lg:min-w-[1rem]";
/** Combobox / custom filter control wrapper — pair with FormCombobox fieldWidth="grow". */ /** Combobox / custom filter control wrapper — pair with FormCombobox fieldWidth="grow". */
export const AUTOTABLE_FILTER_FIELD = "flex flex-col gap-1 min-w-0 w-full overflow-visible [&>label]:text-xs [&>label]:font-medium [&>label]:text-ink [&_.relative]:w-full [&_.relative]:min-w-0 [&_.relative]:flex-none"; export const AUTOTABLE_FILTER_FIELD = "flex flex-col gap-1 min-w-0 w-full overflow-visible [&>label]:text-ss [&>label]:font-medium [&>label]:text-ink [&_.relative]:w-full [&_.relative]:min-w-0 [&_.relative]:flex-none";
/** Sales report toolbar wrappers (presets row 1, filters + export row 2). */ /** Sales report toolbar wrappers (presets row 1, filters + export row 2). */
export const SALES_REPORT_TOOLBAR = "sales-report-toolbar flex flex-col gap-2.5 w-full min-w-0 lg:contents"; export const SALES_REPORT_TOOLBAR = "sales-report-toolbar flex flex-col gap-2.5 w-full min-w-0 lg:contents";
export const SALES_REPORT_PRESETS = "sales-report-toolbar__presets min-w-0 w-full [&_.quick-date-tags]:gap-1.5"; export const SALES_REPORT_PRESETS = "sales-report-toolbar__presets min-w-0 w-full [&_.quick-date-tags]:gap-1.5";
export const SALES_REPORT_FILTERS = "search-fields sales-report-toolbar__filters flex flex-col items-stretch gap-3 w-full overflow-visible lg:flex-row lg:flex-wrap lg:items-end lg:gap-x-3.5 lg:gap-y-2.5 lg:w-auto lg:min-w-0 min-w-0 pt-0.5 border-t border-line [&>*]:min-w-0 [&>*]:max-lg:w-full lg:[&>*]:w-auto lg:[&>*:not(.sales-report-filter-date):not(.sales-report-filter-ref)]:flex-[1_1_8rem] lg:[&>*:not(.sales-report-filter-date):not(.sales-report-filter-ref)]:max-w-[12rem] [&_.relative]:w-full [&_.relative]:min-w-0 [&_.relative]:flex-none"; export const SALES_REPORT_FILTERS = "search-fields sales-report-toolbar__filters flex flex-col items-stretch gap-3 w-full overflow-visible lg:flex-row lg:flex-wrap lg:items-end lg:gap-x-3.5 lg:gap-y-2.5 lg:w-auto lg:min-w-0 min-w-0 pt-0.5 border-t border-line [&>*]:min-w-0 [&>*]:max-lg:w-full lg:[&>*]:w-auto lg:[&>*:not(.sales-report-filter-date):not(.sales-report-filter-ref)]:flex-[1_1_8rem] lg:[&>*:not(.sales-report-filter-date):not(.sales-report-filter-ref)]:max-w-[12rem] [&_.relative]:w-full [&_.relative]:min-w-0 [&_.relative]:flex-none";
export const AUTOTABLE_SEARCH_FIELD = "flex flex-col gap-1 min-w-0 w-full [&>label]:text-xs [&>label]:font-medium [&>label]:text-ink [&_.relative]:w-full [&_.relative]:min-w-0"; export const AUTOTABLE_SEARCH_FIELD = "flex flex-col gap-1 min-w-0 w-full [&>label]:text-ss [&>label]:font-medium [&>label]:text-ink [&_.relative]:w-full [&_.relative]:min-w-0";
export const AUTOTABLE_SEARCH_FIELD_WIDE = AUTOTABLE_SEARCH_FIELD + " lg:flex-[2_1_12rem] lg:max-w-[20rem]"; export const AUTOTABLE_SEARCH_FIELD_WIDE = AUTOTABLE_SEARCH_FIELD + " lg:flex-[2_1_12rem] lg:max-w-[20rem]";
export const AUTOTABLE_SEARCH_FIELD_NARROW = AUTOTABLE_SEARCH_FIELD + " lg:flex-[0_1_auto] lg:max-w-[11rem] lg:min-w-[7rem]"; export const AUTOTABLE_SEARCH_FIELD_NARROW = AUTOTABLE_SEARCH_FIELD + " lg:flex-[0_1_auto] lg:max-w-[11rem] lg:min-w-[7rem]";
export const AUTOTABLE_DATE_SEARCH_FIELD = AUTOTABLE_SEARCH_FIELD + " lg:flex-[1_1_10rem] lg:max-w-none"; export const AUTOTABLE_DATE_SEARCH_FIELD = AUTOTABLE_SEARCH_FIELD + " lg:flex-[1_1_10rem] lg:max-w-none";
@@ -1831,8 +1831,8 @@ function FormulaField(props: FormulaFieldProps) {
</div>; </div>;
// -- Insert menus (Advanced mode), shown in a row above the formula input ---- // -- Insert menus (Advanced mode), shown in a row above the formula input ----
const MENU_TRIGGER = "inline-flex items-center gap-1 px-2 py-1 text-xs rounded-default border border-line-strong bg-surface-muted hover:bg-surface-raised text-ink cursor-pointer leading-none"; const MENU_TRIGGER = "inline-flex items-center gap-1 px-2 py-1 text-ss rounded-default border border-line-strong bg-surface-muted hover:bg-surface-raised text-ink cursor-pointer leading-none";
const MENU_SEARCH = "w-full text-xs p-1 border border-line-strong rounded-default focus:outline-2 focus:outline-sky-500 focus:outline-offset-1"; const MENU_SEARCH = "w-full text-ss p-1 border border-line-strong rounded-default focus:outline-2 focus:outline-sky-500 focus:outline-offset-1";
const columnMenu = () => <Show when={operandOptions.length}> const columnMenu = () => <Show when={operandOptions.length}>
<Popover placement="bottom-start" standalone={true} open={colMenuOpen()} onOpenChange={setColMenuOpen}> <Popover placement="bottom-start" standalone={true} open={colMenuOpen()} onOpenChange={setColMenuOpen}>
@@ -1850,17 +1850,17 @@ function FormulaField(props: FormulaFieldProps) {
class="flex items-center rounded-default hover:bg-surface-raised" class="flex items-center rounded-default hover:bg-surface-raised"
onMouseEnter={() => highlight([o.value])} onMouseLeave={() => highlight(null)}> onMouseEnter={() => highlight([o.value])} onMouseLeave={() => highlight(null)}>
<button type="button" <button type="button"
class="flex-1 min-w-0 text-left truncate px-2 py-1 font-mono text-xs text-sky-600 dark:text-sky-400 bg-transparent border-0 cursor-pointer" class="flex-1 min-w-0 text-left truncate px-2 py-1 font-mono text-ss text-sky-600 dark:text-sky-400 bg-transparent border-0 cursor-pointer"
title={"Insert [" + o.label + "] — this row's cell"} title={"Insert [" + o.label + "] — this row's cell"}
onclick={(_e: MouseEvent) => { insertRef("[" + o.label + "]"); setColMenuOpen(false); }}>[{o.label}]</button> onclick={(_e: MouseEvent) => { insertRef("[" + o.label + "]"); setColMenuOpen(false); }}>[{o.label}]</button>
<button type="button" <button type="button"
class="px-2 py-1 text-xs font-mono text-violet-600 hover:text-violet-800 bg-transparent border-0 cursor-pointer" class="px-2 py-1 text-ss font-mono text-violet-600 hover:text-violet-800 bg-transparent border-0 cursor-pointer"
title={"Insert {" + o.label + "} — whole column"} title={"Insert {" + o.label + "} — whole column"}
onclick={(_e: MouseEvent) => { insertRef("{" + o.label + "}"); setColMenuOpen(false); }}>{"{ }"}</button> onclick={(_e: MouseEvent) => { insertRef("{" + o.label + "}"); setColMenuOpen(false); }}>{"{ }"}</button>
</div>} </div>}
</For> </For>
<Show when={filteredColumns().length === 0}> <Show when={filteredColumns().length === 0}>
<div class="px-2 py-3 text-xs text-ink-muted text-center">No columns match.</div> <div class="px-2 py-3 text-ss text-ink-muted text-center">No columns match.</div>
</Show> </Show>
</div> </div>
</PopoverContent> </PopoverContent>
@@ -1884,14 +1884,14 @@ function FormulaField(props: FormulaFieldProps) {
{(f: { name: string; sig: string; desc: string }) => <button type="button" {(f: { name: string; sig: string; desc: string }) => <button type="button"
class="w-full text-left px-2 py-1 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex flex-col gap-0.5" class="w-full text-left px-2 py-1 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex flex-col gap-0.5"
onclick={(_e: MouseEvent) => { insertFunction(f.name); setFuncMenuOpen(false); }}> onclick={(_e: MouseEvent) => { insertFunction(f.name); setFuncMenuOpen(false); }}>
<span class="font-mono text-xs text-ink" innerHTML={highlightFormula(f.sig)}></span> <span class="font-mono text-ss text-ink" innerHTML={highlightFormula(f.sig)}></span>
<span class="text-[11px] text-ink-muted">{f.desc}</span> <span class="text-[11px] text-ink-muted">{f.desc}</span>
</button>} </button>}
</For> </For>
</div>} </div>}
</For> </For>
<Show when={filteredFunctionGroups().length === 0}> <Show when={filteredFunctionGroups().length === 0}>
<div class="px-2 py-3 text-xs text-ink-muted text-center">No functions match.</div> <div class="px-2 py-3 text-ss text-ink-muted text-center">No functions match.</div>
</Show> </Show>
</div> </div>
</PopoverContent> </PopoverContent>
@@ -1911,12 +1911,12 @@ function FormulaField(props: FormulaFieldProps) {
{(c: { name: string; desc: string }) => <button type="button" {(c: { name: string; desc: string }) => <button type="button"
class="w-full text-left px-2 py-1 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex items-center justify-between gap-2" class="w-full text-left px-2 py-1 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex items-center justify-between gap-2"
onclick={(_e: MouseEvent) => { insertRef(c.name); setConstMenuOpen(false); }}> onclick={(_e: MouseEvent) => { insertRef(c.name); setConstMenuOpen(false); }}>
<span class="font-mono text-xs text-amber-600 dark:text-amber-400">{c.name}</span> <span class="font-mono text-ss text-amber-600 dark:text-amber-400">{c.name}</span>
<span class="text-[11px] text-ink-muted">{c.desc}</span> <span class="text-[11px] text-ink-muted">{c.desc}</span>
</button>} </button>}
</For> </For>
<Show when={filteredConstants().length === 0}> <Show when={filteredConstants().length === 0}>
<div class="px-2 py-3 text-xs text-ink-muted text-center">No constants match.</div> <div class="px-2 py-3 text-ss text-ink-muted text-center">No constants match.</div>
</Show> </Show>
</div> </div>
</PopoverContent> </PopoverContent>
@@ -1948,7 +1948,7 @@ function FormulaField(props: FormulaFieldProps) {
<HoverPopoverTrigger class="inline-flex items-center text-ink-faint hover:text-ink-soft cursor-help bg-transparent border-0 p-0 leading-none"> <HoverPopoverTrigger class="inline-flex items-center text-ink-faint hover:text-ink-soft cursor-help bg-transparent border-0 p-0 leading-none">
<Icon icon="circle-info" size={14} /> <Icon icon="circle-info" size={14} />
</HoverPopoverTrigger> </HoverPopoverTrigger>
<HoverPopoverContent class="p-3 w-80 text-xs text-ink-soft leading-relaxed flex flex-col gap-2"> <HoverPopoverContent class="p-3 w-80 text-ss text-ink-soft leading-relaxed flex flex-col gap-2">
<div> <div>
<div class="font-medium text-ink mb-1">References</div> <div class="font-medium text-ink mb-1">References</div>
<div class="grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 items-center"> <div class="grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 items-center">
@@ -2098,7 +2098,7 @@ function CalculatedColumnForm(props: CalculatedColumnFormProps) {
onHighlight={highlight} onHighlight={highlight}
apiRef={(api: FormulaFieldApi) => { formulaApi = api; }} /> apiRef={(api: FormulaFieldApi) => { formulaApi = api; }} />
<Show when={formulaError()}> <Show when={formulaError()}>
<p class="text-xs text-red-600 dark:text-red-400 mt-1">{formulaError()}</p> <p class="text-ss text-red-600 dark:text-red-400 mt-1">{formulaError()}</p>
</Show> </Show>
</div> </div>
<div class="grid grid-cols-2 gap-2"> <div class="grid grid-cols-2 gap-2">
@@ -2130,11 +2130,11 @@ function CalculatedColumnForm(props: CalculatedColumnFormProps) {
</div> </div>
</div> </div>
<Show when={errorMsg()}> <Show when={errorMsg()}>
<p class="text-xs text-red-600 dark:text-red-400">{errorMsg()}</p> <p class="text-ss text-red-600 dark:text-red-400">{errorMsg()}</p>
</Show> </Show>
<div class="flex gap-2 items-center pt-1"> <div class="flex gap-2 items-center pt-1">
<Show when={!!column() && !!props.onRemove}> <Show when={!!column() && !!props.onRemove}>
<ButtonLinkRed onclick={(_e: MouseEvent) => handleRemove()}><span class="text-xs">Remove</span></ButtonLinkRed> <ButtonLinkRed onclick={(_e: MouseEvent) => handleRemove()}><span class="text-ss">Remove</span></ButtonLinkRed>
</Show> </Show>
<div class="flex gap-2 items-center ml-auto"> <div class="flex gap-2 items-center ml-auto">
<ButtonUI small={true} color={BUTTON_COLOR_LIGHT_NEUTRAL} onclick={(_e: MouseEvent) => close()}>Cancel</ButtonUI> <ButtonUI small={true} color={BUTTON_COLOR_LIGHT_NEUTRAL} onclick={(_e: MouseEvent) => close()}>Cancel</ButtonUI>
@@ -2235,7 +2235,7 @@ function SummaryRowForm(props: SummaryRowFormProps) {
onHighlight={highlight} onHighlight={highlight}
apiRef={(api: FormulaFieldApi) => { formulaApi = api; }} /> apiRef={(api: FormulaFieldApi) => { formulaApi = api; }} />
<Show when={formulaError()}> <Show when={formulaError()}>
<p class="text-xs text-red-600 dark:text-red-400 mt-1">{formulaError()}</p> <p class="text-ss text-red-600 dark:text-red-400 mt-1">{formulaError()}</p>
</Show> </Show>
</div> </div>
<div class="grid grid-cols-2 gap-2"> <div class="grid grid-cols-2 gap-2">
@@ -2257,11 +2257,11 @@ function SummaryRowForm(props: SummaryRowFormProps) {
</div> </div>
</div> </div>
<Show when={errorMsg()}> <Show when={errorMsg()}>
<p class="text-xs text-red-600 dark:text-red-400">{errorMsg()}</p> <p class="text-ss text-red-600 dark:text-red-400">{errorMsg()}</p>
</Show> </Show>
<div class="flex gap-2 items-center pt-1"> <div class="flex gap-2 items-center pt-1">
<Show when={!!row() && !!props.onRemove}> <Show when={!!row() && !!props.onRemove}>
<ButtonLinkRed onclick={(_e: MouseEvent) => handleRemove()}><span class="text-xs">Remove</span></ButtonLinkRed> <ButtonLinkRed onclick={(_e: MouseEvent) => handleRemove()}><span class="text-ss">Remove</span></ButtonLinkRed>
</Show> </Show>
<div class="flex gap-2 items-center ml-auto"> <div class="flex gap-2 items-center ml-auto">
<ButtonUI small={true} color={BUTTON_COLOR_LIGHT_NEUTRAL} onclick={(_e: MouseEvent) => close()}>Cancel</ButtonUI> <ButtonUI small={true} color={BUTTON_COLOR_LIGHT_NEUTRAL} onclick={(_e: MouseEvent) => close()}>Cancel</ButtonUI>
@@ -2303,7 +2303,7 @@ function AddCalcMenu(props: AddCalcMenuProps) {
<Icon icon="table-columns" size={16} class="mt-0.5 text-ink-muted" /> <Icon icon="table-columns" size={16} class="mt-0.5 text-ink-muted" />
<span class="flex flex-col"> <span class="flex flex-col">
<span class="text-sm font-medium text-ink">Calculated column</span> <span class="text-sm font-medium text-ink">Calculated column</span>
<span class="text-xs text-ink-muted">A new column computed for each row</span> <span class="text-ss text-ink-muted">A new column computed for each row</span>
</span> </span>
</button> </button>
</Show> </Show>
@@ -2312,7 +2312,7 @@ function AddCalcMenu(props: AddCalcMenuProps) {
<Icon icon="list-ol" size={16} class="mt-0.5 text-ink-muted" /> <Icon icon="list-ol" size={16} class="mt-0.5 text-ink-muted" />
<span class="flex flex-col"> <span class="flex flex-col">
<span class="text-sm font-medium text-ink">Summary row</span> <span class="text-sm font-medium text-ink">Summary row</span>
<span class="text-xs text-ink-muted">A total or aggregate shown in the footer</span> <span class="text-ss text-ink-muted">A total or aggregate shown in the footer</span>
</span> </span>
</button> </button>
</Show> </Show>

View File

@@ -7,7 +7,7 @@ export const BADGE_AMBER = "amber";
export const BADGE_NEUTRAL = "neutral"; export const BADGE_NEUTRAL = "neutral";
export const BADGE_MUTED = "muted"; export const BADGE_MUTED = "muted";
const BASE = "inline-flex items-center gap-1 text-xs font-semibold py-0.5 px-2 rounded-default whitespace-nowrap"; const BASE = "inline-flex items-center gap-1 text-ss font-semibold py-0.5 px-2 rounded-default whitespace-nowrap";
const COLORS: Record<string, string> = { const COLORS: Record<string, string> = {
"green": "text-white bg-green-700", "green": "text-white bg-green-700",

View File

@@ -144,7 +144,7 @@ export function SegmentedButtons(props: SegmentedButtonsProps) {
const innerRadius = "rounded-default"; // chips: 4px const innerRadius = "rounded-default"; // chips: 4px
const outerRadius = "rounded-md"; // track: 4px + 2px = 6px const outerRadius = "rounded-md"; // track: 4px + 2px = 6px
const sizeCls = () => props.small ? "py-0.5 px-2 text-xs" : "py-1 px-3 text-sm"; const sizeCls = () => props.small ? "py-0.5 px-2 text-ss" : "py-1 px-3 text-sm";
const baseCls = "inline-flex items-center justify-center gap-1.5 flex-1 cursor-pointer font-medium transition-colors whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed"; const baseCls = "inline-flex items-center justify-center gap-1.5 flex-1 cursor-pointer font-medium transition-colors whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed";
const activeCls = "bg-surface text-ink shadow-sm"; const activeCls = "bg-surface text-ink shadow-sm";
const inactiveCls = "text-ink-muted hover:text-ink"; const inactiveCls = "text-ink-muted hover:text-ink";

View File

@@ -21,14 +21,14 @@ export const CAL_MY_MONTH = "text-lg font-heading mx-4 flex-1 text-center font-s
export const CAL_WEEKDAYS_PICKER = "grid grid-cols-7 gap-[2px] mb-1"; export const CAL_WEEKDAYS_PICKER = "grid grid-cols-7 gap-[2px] mb-1";
export const CAL_WEEKDAYS_MONTH = "grid grid-cols-7 border-b border-line"; export const CAL_WEEKDAYS_MONTH = "grid grid-cols-7 border-b border-line";
export const CAL_WEEKDAY_PICKER = "text-center text-xs font-semibold text-ink-muted p-1"; export const CAL_WEEKDAY_PICKER = "text-center text-ss font-semibold text-ink-muted p-1";
export const CAL_WEEKDAY_MONTH = "text-center text-xs font-semibold text-ink-muted p-2 uppercase tracking-wider"; export const CAL_WEEKDAY_MONTH = "text-center text-ss font-semibold text-ink-muted p-2 uppercase tracking-wider";
export const CAL_DAYS_PICKER = "grid grid-cols-7 gap-[2px]"; export const CAL_DAYS_PICKER = "grid grid-cols-7 gap-[2px]";
export const CAL_DAYS_MONTH = "grid grid-cols-7"; export const CAL_DAYS_MONTH = "grid grid-cols-7";
export const CAL_DAY_PICKER_BASE = "aspect-square flex items-center justify-center text-sm bg-transparent border-0 rounded-sm cursor-pointer text-ink p-0"; export const CAL_DAY_PICKER_BASE = "aspect-square flex items-center justify-center text-sm bg-transparent border-0 rounded-sm cursor-pointer text-ink p-0";
export const CAL_DAY_MONTH_BASE = "min-h-[6.5rem] flex flex-col items-stretch justify-start p-1.5 border-r border-b border-line text-left gap-1 text-xs bg-transparent cursor-pointer"; export const CAL_DAY_MONTH_BASE = "min-h-[6.5rem] flex flex-col items-stretch justify-start p-1.5 border-r border-b border-line text-left gap-1 text-ss bg-transparent cursor-pointer";
export const CAL_SELECT = "flex-1 py-1 px-2 text-sm font-semibold border border-line rounded-sm bg-surface text-ink cursor-pointer focus:outline-hidden focus:border-primary"; export const CAL_SELECT = "flex-1 py-1 px-2 text-sm font-semibold border border-line rounded-sm bg-surface text-ink cursor-pointer focus:outline-hidden focus:border-primary";

View File

@@ -1,7 +1,7 @@
import { createSignal, createMemo, untrack, Show, For, JSXElement } from "solid-js"; import { createSignal, createMemo, untrack, Show, For, JSXElement } from "solid-js";
import { Icon } from "./Icons.tsx"; import { Icon } from "./Icons.tsx";
export const GRID_HEADER_CLS = "border-b border-r border-line-strong bg-surface-muted px-1.5 py-1.5 text-left text-xs font-bold uppercase text-ink whitespace-nowrap last:border-r-0"; export const GRID_HEADER_CLS = "border-b border-r border-line-strong bg-surface-muted px-1.5 py-1.5 text-left text-ss font-bold uppercase text-ink whitespace-nowrap last:border-r-0";
interface SortableHeaderProps { interface SortableHeaderProps {
label: string; label: string;
@@ -411,7 +411,7 @@ export function CellGrid(props: CellGridProps) {
); );
}; };
const tableCls = () => "min-w-full w-max border-collapse " + (dense() ? "text-xs" : "text-sm"); const tableCls = () => "min-w-full w-max border-collapse " + (dense() ? "text-ss" : "text-sm");
return ( return (
<div class="relative max-w-full overflow-x-auto border border-line-strong rounded-default bg-surface tabular-nums"> <div class="relative max-w-full overflow-x-auto border border-line-strong rounded-default bg-surface tabular-nums">

View File

@@ -586,7 +586,7 @@ function CartesianTooltip(p: {
}); });
const rows = () => p.props.series.map((s, i) => ({ s, i })).filter(({ i }) => !p.hidden.has(i)); const rows = () => p.props.series.map((s, i) => ({ s, i })).filter(({ i }) => !p.hidden.has(i));
return ( return (
<div class="pointer-events-none absolute z-10 min-w-32 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-xs shadow-lg" <div class="pointer-events-none absolute z-10 min-w-32 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-ss shadow-lg"
style={style()}> style={style()}>
<div class="mb-1 font-medium text-ink">{p.label}</div> <div class="mb-1 font-medium text-ink">{p.label}</div>
<For each={rows()}>{(row) => ( <For each={rows()}>{(row) => (
@@ -704,7 +704,7 @@ function RadialChart(p: SubProps): JSXElement {
<svg width={p.width} height={p.height} viewBox={`0 0 ${p.width} ${p.height}`} class="block" role="img" <svg width={p.width} height={p.height} viewBox={`0 0 ${p.width} ${p.height}`} class="block" role="img"
innerHTML={body()} onpointermove={onMove} onpointerleave={() => setHover(null)} /> innerHTML={body()} onpointermove={onMove} onpointerleave={() => setHover(null)} />
<Show when={p.props.tooltip !== false && hover() !== null}> <Show when={p.props.tooltip !== false && hover() !== null}>
<div class="pointer-events-none absolute z-10 min-w-28 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-xs shadow-lg" <div class="pointer-events-none absolute z-10 min-w-28 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-ss shadow-lg"
style={{ style={{
left: `${clamp(pointer()[0], 8, p.width - 8)}px`, left: `${clamp(pointer()[0], 8, p.width - 8)}px`,
top: `${clamp(pointer()[1], 8, p.height - 8)}px`, top: `${clamp(pointer()[1], 8, p.height - 8)}px`,
@@ -752,7 +752,7 @@ function Legend(p: { props: ChartProps; colorOf: (i: number) => string; hidden:
}> }>
<span class="inline-block h-0.5 w-4 rounded-full" style={{ "background-color": p.colorOf(it.i), opacity: off() ? 0.35 : 1 }} /> <span class="inline-block h-0.5 w-4 rounded-full" style={{ "background-color": p.colorOf(it.i), opacity: off() ? 0.35 : 1 }} />
</Show> </Show>
<span class={"text-xs " + (off() ? "text-ink-faint line-through" : "text-ink-soft")}>{it.name}</span> <span class={"text-ss " + (off() ? "text-ink-faint line-through" : "text-ink-soft")}>{it.name}</span>
</button> </button>
); );
}}</For> }}</For>

View File

@@ -102,7 +102,7 @@ const CRM_TAB_ROW = "flex w-full overflow-x-auto text-sm";
const CRM_TAB_BASE = "flex items-center gap-1.5 cursor-pointer p-4 font-medium border-line-strong transition-colors"; const CRM_TAB_BASE = "flex items-center gap-1.5 cursor-pointer p-4 font-medium border-line-strong transition-colors";
const CRM_TAB_INACTIVE = "border-b text-ink-muted hover:text-ink"; const CRM_TAB_INACTIVE = "border-b text-ink-muted hover:text-ink";
const CRM_TAB_ACTIVE = "border-x border-t-2 border-t-sky-700 text-primary"; const CRM_TAB_ACTIVE = "border-x border-t-2 border-t-sky-700 text-primary";
const CRM_TAB_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-primary text-white rounded-full"; const CRM_TAB_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-ss font-semibold bg-primary text-white rounded-full";
export function CrmTabGroup(props: CrmTabGroupProps) { export function CrmTabGroup(props: CrmTabGroupProps) {
// Memoize props.items: callers pass items={[{content: <Comp/>}]} where each // Memoize props.items: callers pass items={[{content: <Comp/>}]} where each
@@ -148,7 +148,7 @@ const CRM_SUBTAB_BASE = "flex items-center gap-1.5 py-1 px-3 cursor-pointer font
const CRM_SUBTAB_DIVIDER = "border-l border-line-strong"; const CRM_SUBTAB_DIVIDER = "border-l border-line-strong";
const CRM_SUBTAB_ACTIVE = "bg-neutral-500 text-white"; const CRM_SUBTAB_ACTIVE = "bg-neutral-500 text-white";
const CRM_SUBTAB_INACTIVE = "bg-surface text-ink-soft hover:bg-surface-raised hover:text-ink"; const CRM_SUBTAB_INACTIVE = "bg-surface text-ink-soft hover:bg-surface-raised hover:text-ink";
const CRM_SUBTAB_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-black/10 text-current rounded-full"; const CRM_SUBTAB_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-ss font-semibold bg-black/10 text-current rounded-full";
export function CrmSubTabGroup(props: CrmTabGroupProps) { export function CrmSubTabGroup(props: CrmTabGroupProps) {
// See CrmTabGroup: memoize eager `content` JSX so it's built once, in-owner. // See CrmTabGroup: memoize eager `content` JSX so it's built once, in-owner.

View File

@@ -45,8 +45,8 @@ function _prefixCls(small?: boolean, error?: unknown, onDark?: boolean): string
+ (small ? " p-1 text-sm" : " p-2"); + (small ? " p-1 text-sm" : " p-2");
} }
const FORM_ERROR = "block text-red-600 text-xs mt-1"; const FORM_ERROR = "block text-red-600 text-ss mt-1";
const FORM_SUCCESS = "block text-green-600 text-xs mt-1"; const FORM_SUCCESS = "block text-green-600 text-ss mt-1";
const INPUT_GROUP = "flex flex-row items-stretch w-full text-sm"; const INPUT_GROUP = "flex flex-row items-stretch w-full text-sm";
const TRIGGER_BASE = "bg-white border border-neutral-300 rounded-default shadow-xs text-sm w-full text-left flex items-center justify-between gap-2 cursor-pointer disabled:bg-neutral-100 disabled:cursor-not-allowed"; const TRIGGER_BASE = "bg-white border border-neutral-300 rounded-default shadow-xs text-sm w-full text-left flex items-center justify-between gap-2 cursor-pointer disabled:bg-neutral-100 disabled:cursor-not-allowed";
@@ -662,8 +662,8 @@ export function FormSignaturePad(props: { value?: () => string; onchange?: (svg:
onTouchEnd={stopDrawing} onTouchEnd={stopDrawing}
/> />
<div class="flex items-center justify-between px-2 py-1 border-t border-neutral-200 bg-neutral-50"> <div class="flex items-center justify-between px-2 py-1 border-t border-neutral-200 bg-neutral-50">
<span class="text-xs text-neutral-400 italic">{isEmpty() ? "Sign above" : ""}</span> <span class="text-ss text-neutral-400 italic">{isEmpty() ? "Sign above" : ""}</span>
<button type="button" class="text-xs font-medium text-neutral-500 bg-transparent border-none cursor-pointer px-1 py-0.5 rounded-default hover:text-red-600 hover:bg-red-50 disabled:opacity-40 disabled:cursor-not-allowed" onclick={clearSignature} disabled={isEmpty()}>Clear</button> <button type="button" class="text-ss font-medium text-neutral-500 bg-transparent border-none cursor-pointer px-1 py-0.5 rounded-default hover:text-red-600 hover:bg-red-50 disabled:opacity-40 disabled:cursor-not-allowed" onclick={clearSignature} disabled={isEmpty()}>Clear</button>
</div> </div>
</div> </div>
</div>; </div>;
@@ -1537,7 +1537,7 @@ export function FormMultiSelect(props: FormMultiSelectProps) {
} }
return <div ref={(el) => tagsContainerRef = el} class="flex flex-nowrap gap-1 overflow-hidden items-center"> return <div ref={(el) => tagsContainerRef = el} class="flex flex-nowrap gap-1 overflow-hidden items-center">
<For each={selectedOptions()}> <For each={selectedOptions()}>
{(option) => <span class={"inline-flex items-center gap-0.5 bg-neutral-200 rounded-default whitespace-nowrap leading-none" + (props.small ? " py-0.5 px-1.5 text-xs" : " py-0.5 px-2 text-sm")}> {(option) => <span class={"inline-flex items-center gap-0.5 bg-neutral-200 rounded-default whitespace-nowrap leading-none" + (props.small ? " py-0.5 px-1.5 text-ss" : " py-0.5 px-2 text-sm")}>
{option.label} {option.label}
<button type="button" class="bg-transparent border-none p-0 cursor-pointer flex items-center hover:text-neutral-900" onclick={(e) => removeOption(option.value, e)}> <button type="button" class="bg-transparent border-none p-0 cursor-pointer flex items-center hover:text-neutral-900" onclick={(e) => removeOption(option.value, e)}>
<IconInline icon="xmark" size={10} /> <IconInline icon="xmark" size={10} />

View File

@@ -258,7 +258,7 @@ export function FuzzyMatch(props: FuzzyMatchProps) {
const ScoreBadge = (p: { score: number }) => const ScoreBadge = (p: { score: number }) =>
<Show when={props.showScores}> <Show when={props.showScores}>
<span class="ml-3 shrink-0 text-xs text-ink-faint">{p.score}</span> <span class="ml-3 shrink-0 text-ss text-ink-faint">{p.score}</span>
</Show>; </Show>;
return <div ref={containerRef} class={"relative " + (props.class ?? "")}> return <div ref={containerRef} class={"relative " + (props.class ?? "")}>

View File

@@ -21,7 +21,7 @@ interface CodeBoxProps {
export function CodeBox(props: CodeBoxProps): JSXElement { export function CodeBox(props: CodeBoxProps): JSXElement {
return ( return (
<div class={"text-xs p-3 bg-neutral-800 text-neutral-100 border border-neutral-700 rounded-default " + (props.class || "")}> <div class={"text-ss p-3 bg-neutral-800 text-neutral-100 border border-neutral-700 rounded-default " + (props.class || "")}>
<pre><code>{props.code}</code></pre> <pre><code>{props.code}</code></pre>
</div> </div>
); );
@@ -79,7 +79,7 @@ interface BreadcrumbsProps {
export function Breadcrumbs(props: BreadcrumbsProps): JSXElement { export function Breadcrumbs(props: BreadcrumbsProps): JSXElement {
return ( return (
<div class="flex flex-row items-center text-ink-faint text-xs"> <div class="flex flex-row items-center text-ink-faint text-ss">
<For each={props.items}>{(crumb, index) => ( <For each={props.items}>{(crumb, index) => (
index() !== props.items.length - 1 index() !== props.items.length - 1
? ( ? (

View File

@@ -90,7 +90,7 @@ const MODAL_SIZES: Record<ModalSize, string> = {
"2xlarge":"max-w-5xl", "2xlarge":"max-w-5xl",
"3xlarge":"max-w-6xl", "3xlarge":"max-w-6xl",
"4xlarge":"max-w-7xl", "4xlarge":"max-w-7xl",
"5xlarge":"max-w-[90rem]", "5xlarge":"max-w-8xl",
full: "max-w-none", full: "max-w-none",
}; };
@@ -117,12 +117,12 @@ const CONFIRM_OK_VARIANTS = {
const WIZARD_HEADER = "flex flex-col items-center gap-2 flex-1"; const WIZARD_HEADER = "flex flex-col items-center gap-2 flex-1";
const WIZARD_TITLE_ROW = "flex items-center justify-between w-full"; const WIZARD_TITLE_ROW = "flex items-center justify-between w-full";
const WIZARD_TITLE = "text-xl"; const WIZARD_TITLE = "text-xl";
const WIZARD_STEP_NAME = "text-xs font-semibold text-ink-soft uppercase tracking-wider"; const WIZARD_STEP_NAME = "text-ss font-semibold text-ink-soft uppercase tracking-wider";
const WIZARD_STEPS = "flex items-center justify-between relative w-full max-w-64"; const WIZARD_STEPS = "flex items-center justify-between relative w-full max-w-64";
const WIZARD_TRACK = "absolute top-1/2 left-0 right-0 h-0.5 bg-surface-strong -translate-y-1/2"; const WIZARD_TRACK = "absolute top-1/2 left-0 right-0 h-0.5 bg-surface-strong -translate-y-1/2";
const WIZARD_TRACK_FILL = "h-full bg-primary transition-[width] duration-300 ease-in-out"; const WIZARD_TRACK_FILL = "h-full bg-primary transition-[width] duration-300 ease-in-out";
const WIZARD_STEP_WRAP = "relative z-[1]"; const WIZARD_STEP_WRAP = "relative z-[1]";
const STEP_INDICATOR_BASE = "w-7 h-7 rounded-full flex items-center justify-center text-xs font-semibold border-2 shrink-0 transition-all duration-300 ease-in-out"; const STEP_INDICATOR_BASE = "w-7 h-7 rounded-full flex items-center justify-center text-ss font-semibold border-2 shrink-0 transition-all duration-300 ease-in-out";
const STEP_INDICATOR_PENDING = "border-line-strong text-ink-faint bg-surface"; const STEP_INDICATOR_PENDING = "border-line-strong text-ink-faint bg-surface";
const STEP_INDICATOR_ACTIVE = "bg-primary text-white border-primary"; const STEP_INDICATOR_ACTIVE = "bg-primary text-white border-primary";
const STEP_INDICATOR_COMPLETED = "bg-primary text-white border-primary"; const STEP_INDICATOR_COMPLETED = "bg-primary text-white border-primary";

View File

@@ -3,7 +3,7 @@ import { For, createSignal, Show, JSXElement } from "solid-js";
const NAV_ROOT = "bg-surface rounded-default shadow-sm border border-line py-2"; const NAV_ROOT = "bg-surface rounded-default shadow-sm border border-line py-2";
const NAV_LIST = "list-none m-0 p-0"; const NAV_LIST = "list-none m-0 p-0";
const NAV_BTN = "w-full text-left py-2 pr-3 pl-4 text-sm cursor-pointer bg-transparent text-ink-soft border-0 hover:text-ink hover:bg-surface-muted"; const NAV_BTN = "w-full text-left py-2 pr-3 pl-4 text-sm cursor-pointer bg-transparent text-ink-soft border-0 hover:text-ink hover:bg-surface-muted";
const NAV_SUBBTN = "w-full text-left py-1.5 pr-3 pl-8 text-xs cursor-pointer bg-transparent text-ink-muted border-0 hover:text-ink hover:bg-surface-muted"; const NAV_SUBBTN = "w-full text-left py-1.5 pr-3 pl-8 text-ss cursor-pointer bg-transparent text-ink-muted border-0 hover:text-ink hover:bg-surface-muted";
const NAV_ICON = "mr-2"; const NAV_ICON = "mr-2";
const LAYOUT_ROOT = "grid grid-cols-1 gap-8 min-h-screen items-start lg:grid-cols-12"; const LAYOUT_ROOT = "grid grid-cols-1 gap-8 min-h-screen items-start lg:grid-cols-12";

View File

@@ -146,7 +146,7 @@ export function TabGroup(props: TabGroupProps) {
const b = resolveBadge(item.badge); const b = resolveBadge(item.badge);
return b != null && b > 0; return b != null && b > 0;
})()}> })()}>
<span class="inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-primary text-white rounded-full">{resolveBadge(item.badge)}</span> <span class="inline-flex items-center justify-center min-w-5 h-5 px-1 text-ss font-semibold bg-primary text-white rounded-full">{resolveBadge(item.badge)}</span>
</Show> </Show>
</button> </button>
)}</For> )}</For>

View File

@@ -55,7 +55,7 @@ export function ToggleSwitch(props: ToggleSwitchProps) {
class={"text-sm select-none " + (isDisabled() ? "text-ink-faint" : "text-ink")} class={"text-sm select-none " + (isDisabled() ? "text-ink-faint" : "text-ink")}
onclick={(_e: MouseEvent) => toggle()} onclick={(_e: MouseEvent) => toggle()}
>{resolve(props.label)}</span>} >{resolve(props.label)}</span>}
{props.description !== undefined && <span class="text-xs text-ink-muted">{resolve(props.description)}</span>} {props.description !== undefined && <span class="text-ss text-ink-muted">{resolve(props.description)}</span>}
</div>} </div>}
</div>; </div>;
} }

View File

@@ -468,7 +468,7 @@ function TutorialPopover() {
<Show when={displayedStep()?.title}> <Show when={displayedStep()?.title}>
<span class="font-medium text-ink">{displayedStep()?.title}</span> <span class="font-medium text-ink">{displayedStep()?.title}</span>
</Show> </Show>
<span class="text-xs text-ink-muted"> <span class="text-ss text-ink-muted">
{(displayedStepIndex() + 1) + " of " + ctx.totalSteps()} {(displayedStepIndex() + 1) + " of " + ctx.totalSteps()}
</span> </span>
</div> </div>

View File

@@ -195,7 +195,7 @@ export function USHeatmap(props: USHeatmapProps): JSXElement {
<Show when={props.tooltip !== false && tip()}> <Show when={props.tooltip !== false && tip()}>
{(t) => ( {(t) => (
<div class="pointer-events-none absolute z-10 min-w-28 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-xs shadow-lg" <div class="pointer-events-none absolute z-10 min-w-28 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-ss shadow-lg"
style={{ style={{
left: `${clamp(pointer()[0], 8, 100000)}px`, left: `${clamp(pointer()[0], 8, 100000)}px`,
top: `${Math.max(8, pointer()[1])}px`, top: `${Math.max(8, pointer()[1])}px`,
@@ -222,7 +222,7 @@ export function USHeatmap(props: USHeatmapProps): JSXElement {
function ChoroplethLegend(p: { min: number; max: number; steps: number; fmt: (v: number) => string }): JSXElement { function ChoroplethLegend(p: { min: number; max: number; steps: number; fmt: (v: number) => string }): JSXElement {
const swatches = () => Array.from({ length: p.steps }, (_, i) => i + 1); const swatches = () => Array.from({ length: p.steps }, (_, i) => i + 1);
return ( return (
<div class="mt-3 flex items-center gap-2 text-xs text-ink-muted"> <div class="mt-3 flex items-center gap-2 text-ss text-ink-muted">
<span style={{ "font-variant-numeric": "tabular-nums" }}>{p.fmt(p.min)}</span> <span style={{ "font-variant-numeric": "tabular-nums" }}>{p.fmt(p.min)}</span>
<div class="flex overflow-hidden rounded-xs"> <div class="flex overflow-hidden rounded-xs">
<For each={swatches()}>{(k) => ( <For each={swatches()}>{(k) => (

View File

@@ -118,14 +118,14 @@ var atHeaderSortIconCls = map[AutoTableHeaderColor]string{
var HEADER_PADDING_CLS = map[AutoTableSize]string{ var HEADER_PADDING_CLS = map[AutoTableSize]string{
AUTOTABLE_SIZE_DEFAULT: "p-4 text-sm", AUTOTABLE_SIZE_DEFAULT: "p-4 text-sm",
AUTOTABLE_SIZE_COMPACT: "py-1.5 px-2 text-sm", AUTOTABLE_SIZE_COMPACT: "py-1.5 px-2 text-sm",
AUTOTABLE_SIZE_SUPERCOMPACT: "py-1 px-2 text-xs", AUTOTABLE_SIZE_SUPERCOMPACT: "py-1 px-2 text-ss",
} }
// BODY_PADDING_CLS is the body cell padding per table size (applied via [&_td]:). // BODY_PADDING_CLS is the body cell padding per table size (applied via [&_td]:).
var BODY_PADDING_CLS = map[AutoTableSize]string{ var BODY_PADDING_CLS = map[AutoTableSize]string{
AUTOTABLE_SIZE_DEFAULT: "text-sm [&_td]:p-4", AUTOTABLE_SIZE_DEFAULT: "text-sm [&_td]:p-4",
AUTOTABLE_SIZE_COMPACT: "text-sm [&_td]:py-1 [&_td]:px-2", AUTOTABLE_SIZE_COMPACT: "text-sm [&_td]:py-1 [&_td]:px-2",
AUTOTABLE_SIZE_SUPERCOMPACT: "text-xs [&_td]:py-0.5 [&_td]:px-2", AUTOTABLE_SIZE_SUPERCOMPACT: "text-ss [&_td]:py-0.5 [&_td]:px-2",
} }
// atPaginationPaddingCls is the pagination bar padding per table size. // atPaginationPaddingCls is the pagination bar padding per table size.
@@ -2184,7 +2184,7 @@ const (
AUTOTABLE_SEARCH_FIELD = "flex flex-col gap-1 min-w-0 grow sm:grow-0 sm:w-48" AUTOTABLE_SEARCH_FIELD = "flex flex-col gap-1 min-w-0 grow sm:grow-0 sm:w-48"
AUTOTABLE_SEARCH_SELECT = "w-full rounded-default border border-line-strong bg-surface px-3 py-2 text-sm" AUTOTABLE_SEARCH_SELECT = "w-full rounded-default border border-line-strong bg-surface px-3 py-2 text-sm"
AUTOTABLE_FILTERS_TOGGLE = "sm:hidden inline-flex items-center gap-2 rounded-default border border-line-strong px-3 py-2 text-sm" AUTOTABLE_FILTERS_TOGGLE = "sm:hidden inline-flex items-center gap-2 rounded-default border border-line-strong px-3 py-2 text-sm"
AUTOTABLE_FILTERS_BADGE = "ml-1 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-sky-600 px-1.5 text-xs font-semibold text-white" AUTOTABLE_FILTERS_BADGE = "ml-1 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-sky-600 px-1.5 text-ss font-semibold text-white"
AUTOTABLE_TOOLBAR = "flex flex-wrap items-end justify-between gap-3 pb-3" AUTOTABLE_TOOLBAR = "flex flex-wrap items-end justify-between gap-3 pb-3"
AUTOTABLE_ASIDE = "w-full sm:w-64 shrink-0 rounded-default border border-line-strong bg-surface p-3" AUTOTABLE_ASIDE = "w-full sm:w-64 shrink-0 rounded-default border border-line-strong bg-surface p-3"
AUTOTABLE_ACCORDION_CELL = "w-10 text-center" AUTOTABLE_ACCORDION_CELL = "w-10 text-center"
@@ -3141,7 +3141,7 @@ const (
formulaPlaceholder = `<span class="text-ink-faint">e.g. [Revenue] / SUM({Revenue}) * 100</span>` formulaPlaceholder = `<span class="text-ink-faint">e.g. [Revenue] / SUM({Revenue}) * 100</span>`
calcMenuTrigger = "inline-flex items-center gap-1 rounded-default border border-line-strong bg-surface-muted px-2 py-1 text-xs leading-none text-ink-soft cursor-pointer hover:bg-surface-raised" calcMenuTrigger = "inline-flex items-center gap-1 rounded-default border border-line-strong bg-surface-muted px-2 py-1 text-ss leading-none text-ink-soft cursor-pointer hover:bg-surface-raised"
calcChooserItem = "w-full text-left px-2 py-2 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex items-start gap-2.5" calcChooserItem = "w-full text-left px-2 py-2 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex items-start gap-2.5"
) )
@@ -3630,7 +3630,7 @@ func calcChooserButton(icon, title, subtitle string, onClick func()) *vdom.VNode
Icon(icon, 16, "mt-0.5 text-ink-muted"), Icon(icon, 16, "mt-0.5 text-ink-muted"),
vdom.Span(vdom.Attr("class", "flex flex-col"), vdom.Span(vdom.Attr("class", "flex flex-col"),
vdom.Span(vdom.Attr("class", "text-sm font-medium text-ink"), vdom.Text(title)), vdom.Span(vdom.Attr("class", "text-sm font-medium text-ink"), vdom.Text(title)),
vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(subtitle)), vdom.Span(vdom.Attr("class", "text-ss text-ink-muted"), vdom.Text(subtitle)),
), ),
) )
} }
@@ -3639,7 +3639,7 @@ func calcListRow(title, subtitle string, onEdit, onDelete func()) *vdom.VNode {
return vdom.Div(vdom.Attr("class", "flex items-center gap-2 rounded-default px-2 py-1 hover:bg-surface-muted"), return vdom.Div(vdom.Attr("class", "flex items-center gap-2 rounded-default px-2 py-1 hover:bg-surface-muted"),
vdom.Div(vdom.Attr("class", "min-w-0 grow"), vdom.Div(vdom.Attr("class", "min-w-0 grow"),
vdom.Div(vdom.Attr("class", "truncate text-sm font-medium text-ink"), vdom.Text(title)), vdom.Div(vdom.Attr("class", "truncate text-sm font-medium text-ink"), vdom.Text(title)),
vdom.Div(vdom.Attr("class", "truncate font-mono text-xs text-ink-muted"), vdom.Text(subtitle)), vdom.Div(vdom.Attr("class", "truncate font-mono text-ss text-ink-muted"), vdom.Text(subtitle)),
), ),
Button(ButtonProps{Color: ButtonLightNeutral, Small: true, Icon: "pencil", Title: "Edit", OnClick: onEdit}), Button(ButtonProps{Color: ButtonLightNeutral, Small: true, Icon: "pencil", Title: "Edit", OnClick: onEdit}),
Button(ButtonProps{Color: ButtonLightNeutral, Small: true, Icon: "trash", Title: "Delete", OnClick: onDelete}), Button(ButtonProps{Color: ButtonLightNeutral, Small: true, Icon: "trash", Title: "Delete", OnClick: onDelete}),
@@ -3793,7 +3793,7 @@ func (s *AutoTableState) calcForm(e *calcEditor, summary bool) *vdom.VNode {
} }
if msg := e.errorMsg.Get(); msg != "" { if msg := e.errorMsg.Get(); msg != "" {
fields = append(fields, vdom.P(vdom.Attr("class", "text-xs text-red-600 dark:text-red-400"), vdom.Text(msg))) fields = append(fields, vdom.P(vdom.Attr("class", "text-ss text-red-600 dark:text-red-400"), vdom.Text(msg)))
} }
saveLabel := "Add" saveLabel := "Add"
@@ -3897,7 +3897,7 @@ func (s *AutoTableState) calcBasicEditor(e *calcEditor, summary bool) *vdom.VNod
}))) })))
} }
children = append(children, vdom.P(vdom.Attr("class", "text-xs text-ink-muted"), children = append(children, vdom.P(vdom.Attr("class", "text-ss text-ink-muted"),
vdom.Text(calcBasicHint(e, summary)))) vdom.Text(calcBasicHint(e, summary))))
return vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex flex-col gap-2")}, children)...) return vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex flex-col gap-2")}, children)...)
@@ -3929,11 +3929,11 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
colItems = append(colItems, colItems = append(colItems,
e.colMenu.Item(MenuItemProps{OnClick: func() { e.insertAtCaret("[" + label + "]") }}, e.colMenu.Item(MenuItemProps{OnClick: func() { e.insertAtCaret("[" + label + "]") }},
vdom.Span(vdom.Attr("class", "font-mono text-sky-600 dark:text-sky-400"), vdom.Text("["+label+"]")), vdom.Span(vdom.Attr("class", "font-mono text-sky-600 dark:text-sky-400"), vdom.Text("["+label+"]")),
vdom.Span(vdom.Attr("class", "ml-2 text-xs text-ink-muted"), vdom.Text("this row")), vdom.Span(vdom.Attr("class", "ml-2 text-ss text-ink-muted"), vdom.Text("this row")),
), ),
e.colMenu.Item(MenuItemProps{OnClick: func() { e.insertAtCaret("{" + label + "}") }}, e.colMenu.Item(MenuItemProps{OnClick: func() { e.insertAtCaret("{" + label + "}") }},
vdom.Span(vdom.Attr("class", "font-mono text-violet-600 dark:text-violet-400"), vdom.Text("{"+label+"}")), vdom.Span(vdom.Attr("class", "font-mono text-violet-600 dark:text-violet-400"), vdom.Text("{"+label+"}")),
vdom.Span(vdom.Attr("class", "ml-2 text-xs text-ink-muted"), vdom.Text("whole column")), vdom.Span(vdom.Attr("class", "ml-2 text-ss text-ink-muted"), vdom.Text("whole column")),
), ),
) )
} }
@@ -3945,7 +3945,7 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
groups := filterFormulaGroups(e.fnSearch.Get()) groups := filterFormulaGroups(e.fnSearch.Get())
if len(groups) == 0 { if len(groups) == 0 {
fnItems = append(fnItems, vdom.Div( fnItems = append(fnItems, vdom.Div(
vdom.Attr("class", "px-2 py-3 text-center text-xs text-ink-muted"), vdom.Attr("class", "px-2 py-3 text-center text-ss text-ink-muted"),
vdom.Text("No functions match"), vdom.Text("No functions match"),
)) ))
} }
@@ -3959,8 +3959,8 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
vdom.Div(vdom.Attr("class", "flex flex-col items-start gap-0.5 min-w-0"), vdom.Div(vdom.Attr("class", "flex flex-col items-start gap-0.5 min-w-0"),
// The signature is syntax-highlighted with the same highlighter the // The signature is syntax-highlighted with the same highlighter the
// formula box uses, so the menu and the editor speak one language. // formula box uses, so the menu and the editor speak one language.
vdom.Span(vdom.Attr("class", "font-mono text-xs"), vdom.Raw(HighlightFormula(f.Sig))), vdom.Span(vdom.Attr("class", "font-mono text-ss"), vdom.Raw(HighlightFormula(f.Sig))),
vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(f.Desc)), vdom.Span(vdom.Attr("class", "text-ss text-ink-muted"), vdom.Text(f.Desc)),
), ),
)) ))
} }
@@ -3973,8 +3973,8 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
OnClick: func() { e.insertAtCaret(cc.Name) }, OnClick: func() { e.insertAtCaret(cc.Name) },
}, },
vdom.Div(vdom.Attr("class", "flex flex-col items-start gap-0.5"), vdom.Div(vdom.Attr("class", "flex flex-col items-start gap-0.5"),
vdom.Span(vdom.Attr("class", "font-mono text-xs text-amber-600 dark:text-amber-400"), vdom.Text(cc.Name)), vdom.Span(vdom.Attr("class", "font-mono text-ss text-amber-600 dark:text-amber-400"), vdom.Text(cc.Name)),
vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(cc.Desc)), vdom.Span(vdom.Attr("class", "text-ss text-ink-muted"), vdom.Text(cc.Desc)),
), ),
)) ))
} }
@@ -4028,7 +4028,7 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
), ),
) )
help := vdom.P(vdom.Attr("class", "text-xs text-ink-muted"), help := vdom.P(vdom.Attr("class", "text-ss text-ink-muted"),
vdom.Text("[Column] is this row's cell · {Column} is the whole column · {Column:1:ROW()} is a running total.")) vdom.Text("[Column] is this row's cell · {Column} is the whole column · {Column:1:ROW()} is a running total."))
children := []*vdom.VNode{menus, editor, help} children := []*vdom.VNode{menus, editor, help}
@@ -4048,7 +4048,7 @@ func (e *calcEditor) fnSearchBox() *vdom.VNode {
vdom.Input( vdom.Input(
vdom.WithRef(e.fnSearchRef), vdom.WithRef(e.fnSearchRef),
vdom.Attr("type", "text"), vdom.Attr("type", "text"),
vdom.Attr("class", "w-full rounded-default border border-line-strong p-1 text-xs focus:outline-2 focus:outline-sky-500"), vdom.Attr("class", "w-full rounded-default border border-line-strong p-1 text-ss focus:outline-2 focus:outline-sky-500"),
vdom.Attr("placeholder", "Search functions…"), vdom.Attr("placeholder", "Search functions…"),
vdom.Attr("spellcheck", "false"), vdom.Attr("spellcheck", "false"),
vdom.Prop("value", e.fnSearch.Get()), vdom.Prop("value", e.fnSearch.Get()),
@@ -4089,12 +4089,12 @@ func (s *AutoTableState) calcPreview(e *calcEditor, src string, summary bool) *v
return nil return nil
} }
if _, err := CompileFormula(src); err != nil { if _, err := CompileFormula(src); err != nil {
return vdom.P(vdom.Attr("class", "text-xs text-red-600 dark:text-red-400"), vdom.Text(err.Error())) return vdom.P(vdom.Attr("class", "text-ss text-red-600 dark:text-red-400"), vdom.Text(err.Error()))
} }
rows := s.FilteredRows() rows := s.FilteredRows()
if len(rows) == 0 { if len(rows) == 0 {
return vdom.P(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text("Formula is valid.")) return vdom.P(vdom.Attr("class", "text-ss text-ink-muted"), vdom.Text("Formula is valid."))
} }
ctx := NewCalcContext(rows, s.cols, s.Calculated(), s.read) ctx := NewCalcContext(rows, s.cols, s.Calculated(), s.read)
@@ -4107,10 +4107,10 @@ func (s *AutoTableState) calcPreview(e *calcEditor, src string, summary bool) *v
n, err := EvalFormula(src, target) n, err := EvalFormula(src, target)
if err != nil { if err != nil {
return vdom.P(vdom.Attr("class", "text-xs text-red-600 dark:text-red-400"), vdom.Text(err.Error())) return vdom.P(vdom.Attr("class", "text-ss text-red-600 dark:text-red-400"), vdom.Text(err.Error()))
} }
out := FormatCalcResult(n, CalculatedDataType(e.dataType.Get()), calcPrecisionOf(e.precision.Get()), "", "", "") out := FormatCalcResult(n, CalculatedDataType(e.dataType.Get()), calcPrecisionOf(e.precision.Get()), "", "", "")
return vdom.P(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(prefix+out)) return vdom.P(vdom.Attr("class", "text-ss text-ink-muted"), vdom.Text(prefix+out))
} }
// insertAtCaret drops text where the cursor is, rather than at the end — which is // insertAtCaret drops text where the cursor is, rather than at the end — which is
@@ -4141,7 +4141,7 @@ func (e *calcEditor) insertAtCaretOffset(text string, back int) {
func calcField(label string, children ...*vdom.VNode) *vdom.VNode { func calcField(label string, children ...*vdom.VNode) *vdom.VNode {
nodes := []*vdom.VNode{ nodes := []*vdom.VNode{
vdom.Span(vdom.Attr("class", "text-xs font-medium text-ink-soft"), vdom.Text(label)), vdom.Span(vdom.Attr("class", "text-ss font-medium text-ink-soft"), vdom.Text(label)),
} }
nodes = append(nodes, children...) nodes = append(nodes, children...)
return vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex flex-col gap-1")}, nodes)...) return vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex flex-col gap-1")}, nodes)...)

View File

@@ -13,7 +13,7 @@ const (
BadgeMuted = "muted" BadgeMuted = "muted"
) )
const badgeBase = "inline-flex items-center gap-1 text-xs font-semibold py-0.5 px-2 rounded-default whitespace-nowrap" const badgeBase = "inline-flex items-center gap-1 text-ss font-semibold py-0.5 px-2 rounded-default whitespace-nowrap"
var badgeColors = map[string]string{ var badgeColors = map[string]string{
"green": "text-white bg-green-700", "green": "text-white bg-green-700",

View File

@@ -171,7 +171,7 @@ type SegmentedButtonOption struct {
func SegmentedButtons(options []SegmentedButtonOption, value string, onChange func(string), small bool, class string) *vdom.VNode { func SegmentedButtons(options []SegmentedButtonOption, value string, onChange func(string), small bool, class string) *vdom.VNode {
sizeCls := "py-1 px-3 text-sm" sizeCls := "py-1 px-3 text-sm"
if small { if small {
sizeCls = "py-0.5 px-2 text-xs" sizeCls = "py-0.5 px-2 text-ss"
} }
const baseCls = "inline-flex items-center justify-center gap-1.5 flex-1 cursor-pointer font-medium transition-colors whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed" const baseCls = "inline-flex items-center justify-center gap-1.5 flex-1 cursor-pointer font-medium transition-colors whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed"
const activeCls = "bg-surface text-text-heading shadow-sm" const activeCls = "bg-surface text-text-heading shadow-sm"

View File

@@ -38,14 +38,14 @@ const calMyMonth = "text-lg font-heading mx-4 flex-1 text-center font-semibold t
const calWeekdaysPicker = "grid grid-cols-7 gap-[2px] mb-1" const calWeekdaysPicker = "grid grid-cols-7 gap-[2px] mb-1"
const calWeekdaysMonth = "grid grid-cols-7 border-b border-line" const calWeekdaysMonth = "grid grid-cols-7 border-b border-line"
const calWeekdayPicker = "text-center text-xs font-semibold text-text-muted p-1" const calWeekdayPicker = "text-center text-ss font-semibold text-text-muted p-1"
const calWeekdayMonth = "text-center text-xs font-semibold text-text-muted p-2 uppercase tracking-wider" const calWeekdayMonth = "text-center text-ss font-semibold text-text-muted p-2 uppercase tracking-wider"
const calDaysPicker = "grid grid-cols-7 gap-[2px]" const calDaysPicker = "grid grid-cols-7 gap-[2px]"
const calDaysMonth = "grid grid-cols-7" const calDaysMonth = "grid grid-cols-7"
const calDayPickerBase = "aspect-square flex items-center justify-center text-sm bg-transparent border-0 rounded-sm cursor-pointer text-text-body p-0" const calDayPickerBase = "aspect-square flex items-center justify-center text-sm bg-transparent border-0 rounded-sm cursor-pointer text-text-body p-0"
const calDayMonthBase = "min-h-[6.5rem] flex flex-col items-stretch justify-start p-1.5 border-r border-b border-line text-left gap-1 text-xs bg-transparent cursor-pointer" const calDayMonthBase = "min-h-[6.5rem] flex flex-col items-stretch justify-start p-1.5 border-r border-b border-line text-left gap-1 text-ss bg-transparent cursor-pointer"
const calSelect = "flex-1 py-1 px-2 text-sm font-semibold border border-line rounded-sm bg-surface text-text-heading cursor-pointer focus:outline-hidden focus:border-primary" const calSelect = "flex-1 py-1 px-2 text-sm font-semibold border border-line rounded-sm bg-surface text-text-heading cursor-pointer focus:outline-hidden focus:border-primary"

View File

@@ -35,7 +35,7 @@ import (
// GridHeaderCls is the base <th> class for CellGrid headers (exported, matching // GridHeaderCls is the base <th> class for CellGrid headers (exported, matching
// the TSX GRID_HEADER_CLS). // the TSX GRID_HEADER_CLS).
const GridHeaderCls = "border-b border-r border-line-strong bg-surface-muted px-1.5 py-1.5 text-left text-xs font-bold uppercase text-ink whitespace-nowrap last:border-r-0" const GridHeaderCls = "border-b border-r border-line-strong bg-surface-muted px-1.5 py-1.5 text-left text-ss font-bold uppercase text-ink whitespace-nowrap last:border-r-0"
var cgLeadingDigits = regexp.MustCompile(`^\d+`) var cgLeadingDigits = regexp.MustCompile(`^\d+`)
var cgLeadingFloat = regexp.MustCompile(`^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?`) var cgLeadingFloat = regexp.MustCompile(`^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?`)
@@ -467,7 +467,7 @@ func CellGrid(p CellGridProps) *vdom.VNode {
tableCls := "min-w-full w-max border-collapse text-sm" tableCls := "min-w-full w-max border-collapse text-sm"
if p.Dense { if p.Dense {
tableCls = "min-w-full w-max border-collapse text-xs" tableCls = "min-w-full w-max border-collapse text-ss"
} }
return vdom.Div(vdom.Attr("class", "relative max-w-full overflow-x-auto border border-line-strong rounded-default bg-surface tabular-nums"), return vdom.Div(vdom.Attr("class", "relative max-w-full overflow-x-auto border border-line-strong rounded-default bg-surface tabular-nums"),
vdom.Table(vdom.Attr("class", tableCls), thead, tbody), vdom.Table(vdom.Attr("class", tableCls), thead, tbody),

View File

@@ -308,7 +308,7 @@ func (c *Chart) positionTip(left, top float64, transform string) {
// pass so its ref stays valid; onMove positions it imperatively. When there is no hover it // pass so its ref stays valid; onMove positions it imperatively. When there is no hover it
// is present but hidden — the same slot each render keeps the reconciler's diff stable. // is present but hidden — the same slot each render keeps the reconciler's diff stable.
func (c *Chart) tooltipNode(p ChartProps, hv int) *vdom.VNode { func (c *Chart) tooltipNode(p ChartProps, hv int) *vdom.VNode {
base := "pointer-events-none absolute z-10 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-xs shadow-lg" base := "pointer-events-none absolute z-10 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-ss shadow-lg"
if p.NoTooltip || hv < 0 || hv >= chartN(p) { if p.NoTooltip || hv < 0 || hv >= chartN(p) {
return vdom.Div(vdom.WithRef(c.tipRef), vdom.Attr("class", cx(base, "hidden"))) return vdom.Div(vdom.WithRef(c.tipRef), vdom.Attr("class", cx(base, "hidden")))
} }
@@ -1086,10 +1086,10 @@ func (c *Chart) legend(p ChartProps) *vdom.VNode {
it := it // capture per iteration for the click closure it := it // capture per iteration for the click closure
off := !chartShown(p, it.i) off := !chartShown(p, it.i)
swatchStyle := "background-color:" + chartColor(p, it.i) swatchStyle := "background-color:" + chartColor(p, it.i)
labelClass := "text-xs text-ink-soft" labelClass := "text-ss text-ink-soft"
if off { if off {
swatchStyle += ";opacity:0.35" swatchStyle += ";opacity:0.35"
labelClass = "text-xs text-ink-faint line-through" labelClass = "text-ss text-ink-faint line-through"
} }
nodes = append(nodes, vdom.El("button", nodes = append(nodes, vdom.El("button",
vdom.Attr("type", "button"), vdom.Attr("type", "button"),

View File

@@ -52,7 +52,7 @@ const crmTabRow = "flex w-full overflow-x-auto text-sm"
const crmTabBase = "flex items-center gap-1.5 cursor-pointer p-4 font-medium border-line-strong transition-colors" const crmTabBase = "flex items-center gap-1.5 cursor-pointer p-4 font-medium border-line-strong transition-colors"
const crmTabInactive = "border-b text-ink-muted hover:text-ink" const crmTabInactive = "border-b text-ink-muted hover:text-ink"
const crmTabActive = "border-x border-t-2 border-t-sky-700 text-accent" const crmTabActive = "border-x border-t-2 border-t-sky-700 text-accent"
const crmTabBadge = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-primary text-white rounded-full" const crmTabBadge = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-ss font-semibold bg-primary text-white rounded-full"
// CrmTabGroup renders boxed tabs with a sky-blue top accent; content sits flat // CrmTabGroup renders boxed tabs with a sky-blue top accent; content sits flat
// beneath the row (no body panel), with a trailing filler extending the baseline. // beneath the row (no body panel), with a trailing filler extending the baseline.
@@ -95,7 +95,7 @@ const crmSubTabBase = "flex items-center gap-1.5 py-1 px-3 cursor-pointer font-m
const crmSubTabDivider = "border-l border-line-strong" const crmSubTabDivider = "border-l border-line-strong"
const crmSubTabActive = "bg-neutral-500 text-white" const crmSubTabActive = "bg-neutral-500 text-white"
const crmSubTabInactive = "bg-surface text-ink-soft hover:bg-surface-raised hover:text-ink" const crmSubTabInactive = "bg-surface text-ink-soft hover:bg-surface-raised hover:text-ink"
const crmSubTabBadge = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-black/10 text-current rounded-full" const crmSubTabBadge = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-ss font-semibold bg-black/10 text-current rounded-full"
// CrmSubTabGroup renders a left-aligned segmented control (interlocking // CrmSubTabGroup renders a left-aligned segmented control (interlocking
// segments) with a full-width baseline separating the bar from the content. // segments) with a full-width baseline separating the bar from the content.

View File

@@ -26,8 +26,8 @@ import (
const formInputBase = "bg-surface block w-full border rounded-default shadow-xs text-sm focus:outline-2 focus:outline-offset-1 disabled:bg-surface-raised disabled:cursor-not-allowed" const formInputBase = "bg-surface block w-full border rounded-default shadow-xs text-sm focus:outline-2 focus:outline-offset-1 disabled:bg-surface-raised disabled:cursor-not-allowed"
const formInputBaseDark = "bg-dark text-text-on-dark placeholder:text-text-on-dark-faint block w-full border rounded-default shadow-xs text-sm focus:outline-2 focus:outline-offset-1 disabled:opacity-50 disabled:cursor-not-allowed" const formInputBaseDark = "bg-dark text-text-on-dark placeholder:text-text-on-dark-faint block w-full border rounded-default shadow-xs text-sm focus:outline-2 focus:outline-offset-1 disabled:opacity-50 disabled:cursor-not-allowed"
const formErrorCls = "block text-red-600 dark:text-red-400 text-xs mt-1" const formErrorCls = "block text-red-600 dark:text-red-400 text-ss mt-1"
const formSuccessCls = "block text-green-600 text-xs mt-1" const formSuccessCls = "block text-green-600 text-ss mt-1"
const formInputGroupCls = "flex flex-row items-stretch w-full text-sm" const formInputGroupCls = "flex flex-row items-stretch w-full text-sm"
const formTriggerBase = "bg-surface border border-line-strong rounded-default shadow-xs text-sm w-full text-left flex items-center justify-between gap-2 cursor-pointer disabled:bg-surface-raised disabled:cursor-not-allowed" const formTriggerBase = "bg-surface border border-line-strong rounded-default shadow-xs text-sm w-full text-left flex items-center justify-between gap-2 cursor-pointer disabled:bg-surface-raised disabled:cursor-not-allowed"
@@ -1312,7 +1312,7 @@ func (m *MultiSelect) triggerFace(p FormMultiSelectProps, selected []FormSelectO
pillCls := formMultiSelectTag pillCls := formMultiSelectTag
if p.Small { if p.Small {
pillCls = cx(pillCls, "px-1.5 text-xs") pillCls = cx(pillCls, "px-1.5 text-ss")
} else { } else {
pillCls = cx(pillCls, "px-2 text-sm") pillCls = cx(pillCls, "px-2 text-sm")
} }

View File

@@ -275,7 +275,7 @@ func fuzzyScoreBadge(show bool, score int) *vdom.VNode {
if !show { if !show {
return nil return nil
} }
return vdom.Span(vdom.Attr("class", "ml-3 shrink-0 text-xs text-ink-faint"), vdom.Text(strconv.Itoa(score))) return vdom.Span(vdom.Attr("class", "ml-3 shrink-0 text-ss text-ink-faint"), vdom.Text(strconv.Itoa(score)))
} }
// fuzzyListView renders the inline "list" display. Before the user types, all // fuzzyListView renders the inline "list" display. Before the user types, all

View File

@@ -15,7 +15,7 @@ func Divider() *vdom.VNode { return vdom.Hr(vdom.Attr("class", "text-line mt-1 m
// CodeBox renders a dark monospace code block. // CodeBox renders a dark monospace code block.
func CodeBox(code, class string) *vdom.VNode { func CodeBox(code, class string) *vdom.VNode {
return vdom.Div(vdom.Attr("class", cx("text-xs p-3 bg-neutral-800 text-neutral-100 border border-neutral-700 rounded-default", class)), return vdom.Div(vdom.Attr("class", cx("text-ss p-3 bg-neutral-800 text-neutral-100 border border-neutral-700 rounded-default", class)),
vdom.Pre(vdom.Code(vdom.Text(code))), vdom.Pre(vdom.Code(vdom.Text(code))),
) )
} }
@@ -58,7 +58,7 @@ type BreadcrumbItem struct {
// Breadcrumbs renders a chevron-separated crumb trail; the last item is bold and // Breadcrumbs renders a chevron-separated crumb trail; the last item is bold and
// not linked. // not linked.
func Breadcrumbs(items []BreadcrumbItem) *vdom.VNode { func Breadcrumbs(items []BreadcrumbItem) *vdom.VNode {
mods := []vdom.Mod{vdom.Attr("class", "flex flex-row items-center text-ink-faint text-xs")} mods := []vdom.Mod{vdom.Attr("class", "flex flex-row items-center text-ink-faint text-ss")}
for i, crumb := range items { for i, crumb := range items {
if i != len(items)-1 { if i != len(items)-1 {
mods = append(mods, vdom.Span(vdom.Attr("class", "flex items-center"), mods = append(mods, vdom.Span(vdom.Attr("class", "flex items-center"),

View File

@@ -109,7 +109,7 @@ var modalSizes = map[ModalSize]string{
Modal2XLarge: "max-w-5xl", Modal2XLarge: "max-w-5xl",
Modal3XLarge: "max-w-6xl", Modal3XLarge: "max-w-6xl",
Modal4XLarge: "max-w-7xl", Modal4XLarge: "max-w-7xl",
Modal5XLarge: "max-w-[90rem]", Modal5XLarge: "max-w-8xl",
ModalFull: "max-w-none", ModalFull: "max-w-none",
} }
@@ -137,12 +137,12 @@ var modalConfirmOkVariants = map[string]string{
const modalWizardHeader = "flex flex-col items-center gap-2 flex-1" const modalWizardHeader = "flex flex-col items-center gap-2 flex-1"
const modalWizardTitleRow = "flex items-center justify-between w-full" const modalWizardTitleRow = "flex items-center justify-between w-full"
const modalWizardTitle = "text-xl" const modalWizardTitle = "text-xl"
const modalWizardStepName = "text-xs font-semibold text-ink-soft uppercase tracking-wider" const modalWizardStepName = "text-ss font-semibold text-ink-soft uppercase tracking-wider"
const modalWizardSteps = "flex items-center justify-between relative w-full max-w-64" const modalWizardSteps = "flex items-center justify-between relative w-full max-w-64"
const modalWizardTrack = "absolute top-1/2 left-0 right-0 h-0.5 bg-surface-strong -translate-y-1/2" const modalWizardTrack = "absolute top-1/2 left-0 right-0 h-0.5 bg-surface-strong -translate-y-1/2"
const modalWizardTrackFill = "h-full bg-primary transition-[width] duration-300 ease-in-out" const modalWizardTrackFill = "h-full bg-primary transition-[width] duration-300 ease-in-out"
const modalWizardStepWrap = "relative z-[1]" const modalWizardStepWrap = "relative z-[1]"
const modalStepIndicatorBase = "w-7 h-7 rounded-full flex items-center justify-center text-xs font-semibold border-2 shrink-0 transition-all duration-300 ease-in-out" const modalStepIndicatorBase = "w-7 h-7 rounded-full flex items-center justify-center text-ss font-semibold border-2 shrink-0 transition-all duration-300 ease-in-out"
const modalStepIndicatorPending = "border-line-strong text-ink-faint bg-surface" const modalStepIndicatorPending = "border-line-strong text-ink-faint bg-surface"
const modalStepIndicatorActive = "bg-primary text-white border-primary" const modalStepIndicatorActive = "bg-primary text-white border-primary"
const modalStepIndicatorCompleted = "bg-primary text-white border-primary" const modalStepIndicatorCompleted = "bg-primary text-white border-primary"

View File

@@ -80,13 +80,13 @@ var ptRowHoverCls = map[PrettyTableHeaderColor]string{
var ptHeaderPaddingCls = map[PrettyTableSize]string{ var ptHeaderPaddingCls = map[PrettyTableSize]string{
PrettyTableSizeDefault: "p-4 text-sm", PrettyTableSizeDefault: "p-4 text-sm",
PrettyTableSizeCompact: "py-1.5 px-2 text-sm", PrettyTableSizeCompact: "py-1.5 px-2 text-sm",
PrettyTableSizeSuperCompact: "py-1 px-2 text-xs", PrettyTableSizeSuperCompact: "py-1 px-2 text-ss",
} }
var ptBodyPaddingCls = map[PrettyTableSize]string{ var ptBodyPaddingCls = map[PrettyTableSize]string{
PrettyTableSizeDefault: "text-sm [&_td]:p-4", PrettyTableSizeDefault: "text-sm [&_td]:p-4",
PrettyTableSizeCompact: "text-sm [&_td]:py-1 [&_td]:px-2", PrettyTableSizeCompact: "text-sm [&_td]:py-1 [&_td]:px-2",
PrettyTableSizeSuperCompact: "text-xs [&_td]:py-0.5 [&_td]:px-2", PrettyTableSizeSuperCompact: "text-ss [&_td]:py-0.5 [&_td]:px-2",
} }
var ptPosCls = map[PrettyTableColumnPosition]string{ var ptPosCls = map[PrettyTableColumnPosition]string{

View File

@@ -7,7 +7,7 @@ import "kjol/vdom"
const sidebarNavRoot = "bg-surface rounded-default shadow-sm border border-line py-2" const sidebarNavRoot = "bg-surface rounded-default shadow-sm border border-line py-2"
const sidebarNavList = "list-none m-0 p-0" const sidebarNavList = "list-none m-0 p-0"
const sidebarNavBtn = "w-full text-left py-2 pr-3 pl-4 text-sm cursor-pointer bg-transparent text-ink-soft border-0 hover:text-ink hover:bg-surface-muted" const sidebarNavBtn = "w-full text-left py-2 pr-3 pl-4 text-sm cursor-pointer bg-transparent text-ink-soft border-0 hover:text-ink hover:bg-surface-muted"
const sidebarNavSubBtn = "w-full text-left py-1.5 pr-3 pl-8 text-xs cursor-pointer bg-transparent text-ink-muted border-0 hover:text-ink hover:bg-surface-muted" const sidebarNavSubBtn = "w-full text-left py-1.5 pr-3 pl-8 text-ss cursor-pointer bg-transparent text-ink-muted border-0 hover:text-ink hover:bg-surface-muted"
const sidebarNavIcon = "mr-2" const sidebarNavIcon = "mr-2"
const sidebarLayoutRoot = "grid grid-cols-1 gap-8 min-h-screen items-start lg:grid-cols-12" const sidebarLayoutRoot = "grid grid-cols-1 gap-8 min-h-screen items-start lg:grid-cols-12"

View File

@@ -135,7 +135,7 @@ func (s *SignaturePad) Render(p SignaturePadProps) *vdom.VNode {
} }
clear := []vdom.Mod{ clear := []vdom.Mod{
vdom.Attr("type", "button"), vdom.Attr("type", "button"),
vdom.Attr("class", "text-xs text-ink-muted hover:text-ink disabled:opacity-50"), vdom.Attr("class", "text-ss text-ink-muted hover:text-ink disabled:opacity-50"),
vdom.Text(pick(p.ClearText, "Clear")), vdom.Text(pick(p.ClearText, "Clear")),
} }
if p.Disabled || s.IsEmpty() { if p.Disabled || s.IsEmpty() {
@@ -145,7 +145,7 @@ func (s *SignaturePad) Render(p SignaturePadProps) *vdom.VNode {
} }
kids = append(kids, vdom.Div( kids = append(kids, vdom.Div(
vdom.Attr("class", "flex items-center justify-between border-t border-line bg-surface-muted px-2 py-1"), vdom.Attr("class", "flex items-center justify-between border-t border-line bg-surface-muted px-2 py-1"),
vdom.Span(vdom.Attr("class", "text-xs italic text-ink-faint"), vdom.Text(hint)), vdom.Span(vdom.Attr("class", "text-ss italic text-ink-faint"), vdom.Text(hint)),
vdom.Button(clear...), vdom.Button(clear...),
)) ))
} }

View File

@@ -113,7 +113,7 @@ func TabGroup(p TabGroupProps) *vdom.VNode {
vdom.Text(item.Title), vdom.Text(item.Title),
} }
if item.Badge > 0 { if item.Badge > 0 {
btn = append(btn, vdom.Span(vdom.Attr("class", "inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-primary text-white rounded-full"), btn = append(btn, vdom.Span(vdom.Attr("class", "inline-flex items-center justify-center min-w-5 h-5 px-1 text-ss font-semibold bg-primary text-white rounded-full"),
vdom.Text(strconv.Itoa(item.Badge)))) vdom.Text(strconv.Itoa(item.Badge))))
} }
header = append(header, vdom.Button(btn...)) header = append(header, vdom.Button(btn...))

View File

@@ -56,7 +56,7 @@ func ToggleSwitch(checked bool, onChange func(bool), label, description string,
vdom.Text(label))) vdom.Text(label)))
} }
if description != "" { if description != "" {
text = append(text, vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(description))) text = append(text, vdom.Span(vdom.Attr("class", "text-ss text-ink-muted"), vdom.Text(description)))
} }
mods = append(mods, vdom.Div(text...)) mods = append(mods, vdom.Div(text...))
} }

View File

@@ -704,7 +704,7 @@ func (t *Tutorial) content(step *TutorialStep, idx, total int) *vdom.VNode {
headerLeft = append(headerLeft, vdom.Span(vdom.Attr("class", "font-medium text-ink"), headerLeft = append(headerLeft, vdom.Span(vdom.Attr("class", "font-medium text-ink"),
vdom.Text(step.Title))) vdom.Text(step.Title)))
} }
headerLeft = append(headerLeft, vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"), headerLeft = append(headerLeft, vdom.Span(vdom.Attr("class", "text-ss text-ink-muted"),
vdom.Text(strconv.Itoa(idx+1)+" of "+strconv.Itoa(total)))) vdom.Text(strconv.Itoa(idx+1)+" of "+strconv.Itoa(total))))
header := vdom.Div(vdom.Attr("class", "flex items-center justify-between p-4 pb-2"), header := vdom.Div(vdom.Attr("class", "flex items-center justify-between p-4 pb-2"),

View File

@@ -221,7 +221,7 @@ func (c *USHeatmap) onMove(e vdom.Event) {
// tooltipNode is the HTML tooltip (absolute, pointer-events-none), rendered every pass so // tooltipNode is the HTML tooltip (absolute, pointer-events-none), rendered every pass so
// its ref stays valid; onMove positions it imperatively. Hidden when nothing is hovered. // its ref stays valid; onMove positions it imperatively. Hidden when nothing is hovered.
func (c *USHeatmap) tooltipNode(p USHeatmapProps, hv usHover, mn, mx float64, steps int) *vdom.VNode { func (c *USHeatmap) tooltipNode(p USHeatmapProps, hv usHover, mn, mx float64, steps int) *vdom.VNode {
base := "pointer-events-none absolute z-10 min-w-28 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-xs shadow-lg" base := "pointer-events-none absolute z-10 min-w-28 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-ss shadow-lg"
if p.NoTooltip || hv.kind == "" { if p.NoTooltip || hv.kind == "" {
return vdom.Div(vdom.WithRef(c.tipRef), vdom.Attr("class", cx(base, "hidden"))) return vdom.Div(vdom.WithRef(c.tipRef), vdom.Attr("class", cx(base, "hidden")))
} }
@@ -387,7 +387,7 @@ func choroplethLegend(mn, mx float64, steps int, fmt func(float64) string) *vdom
vdom.Attr("style", "background-color:var(--color-choropleth-"+strconv.Itoa(k)+")"))) vdom.Attr("style", "background-color:var(--color-choropleth-"+strconv.Itoa(k)+")")))
} }
ramp := vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex overflow-hidden rounded-xs")}, swatches)...) ramp := vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex overflow-hidden rounded-xs")}, swatches)...)
return vdom.Div(vdom.Attr("class", "mt-3 flex items-center gap-2 text-xs text-ink-muted"), return vdom.Div(vdom.Attr("class", "mt-3 flex items-center gap-2 text-ss text-ink-muted"),
vdom.Span(vdom.Attr("style", "font-variant-numeric:tabular-nums"), vdom.Text(fmt(mn))), vdom.Span(vdom.Attr("style", "font-variant-numeric:tabular-nums"), vdom.Text(fmt(mn))),
ramp, ramp,
vdom.Span(vdom.Attr("style", "font-variant-numeric:tabular-nums"), vdom.Text(fmt(mx))), vdom.Span(vdom.Attr("style", "font-variant-numeric:tabular-nums"), vdom.Text(fmt(mx))),