Small
diff --git a/web/src/lib/components/ui/Button.svelte b/web/src/lib/components/ui/Button.svelte
index c3773f73..d9cf8fca 100644
--- a/web/src/lib/components/ui/Button.svelte
+++ b/web/src/lib/components/ui/Button.svelte
@@ -7,7 +7,6 @@
"transition-colors duration-150 ease-in-out",
"hover:no-underline",
"focus-visible:outline-2 focus-visible:outline-offset-2",
- // each variant paints its own disabled surface (see the variant blocks) —
// Figma models disabled as a distinct fill/text pair, not a dimmed enabled state
"disabled:pointer-events-none aria-disabled:pointer-events-none"
],
@@ -33,10 +32,8 @@
"focus-visible:outline-border-focus",
"disabled:text-foreground-disabled aria-disabled:text-foreground-disabled"
],
- // danger / success / warning are outline buttons in Figma: default surface +
- // border, colored text only (bound to the fg-* tokens), no accent fill. They
- // keep that surface when disabled and only fade the label — the outline is
- // what carries the variant, so dropping it would read as a different button.
+ // the outline is what carries the variant, so dropping it would read as a
+ // different button.
danger: [
"border border-border-default bg-background-default text-foreground-danger",
"hover:bg-background-subtle",
@@ -73,8 +70,6 @@
}
},
compoundVariants: [
- // raised variants draw inset depth via a ::before layer. pressing shifts the
- // content wrapper rather than the button itself, so the box stays put.
{
variant: "default",
insetShadow: true,
@@ -156,8 +151,7 @@
{#snippet content()}
+ the transition matches the ::before shadow's so both halves of the press ease together. -->
diff --git a/web/src/lib/components/ui/ButtonCard.svelte b/web/src/lib/components/ui/ButtonCard.svelte
index c42eb863..f85a4d1a 100644
--- a/web/src/lib/components/ui/ButtonCard.svelte
+++ b/web/src/lib/components/ui/ButtonCard.svelte
@@ -23,7 +23,6 @@
}
},
compoundVariants: [
- // raised variants draw inset depth via a ::before layer.
{
variant: "default",
insetShadow: true,
diff --git a/web/src/lib/components/ui/Checkbox.stories.svelte b/web/src/lib/components/ui/Checkbox.stories.svelte
index 7649ff3d..4bc3d559 100644
--- a/web/src/lib/components/ui/Checkbox.stories.svelte
+++ b/web/src/lib/components/ui/Checkbox.stories.svelte
@@ -19,7 +19,6 @@
});
-
Item
Item
Item
diff --git a/web/src/lib/components/ui/Combobox.svelte b/web/src/lib/components/ui/Combobox.svelte
index c94941da..6b925297 100644
--- a/web/src/lib/components/ui/Combobox.svelte
+++ b/web/src/lib/components/ui/Combobox.svelte
@@ -47,13 +47,10 @@
* elsewhere — a ref in the url, say — and let `onSelect` do the navigating. */
value?: string;
onSelect?: (value: string) => void;
- /** trigger text while nothing is selected */
placeholder?: string;
searchPlaceholder?: string;
emptyLabel?: string;
- /** names the trigger and the listbox for screen readers */
label?: string;
- /** decorative glyph on the trigger's leading edge, standing for the current selection */
icon?: Component;
/** take filtering over, e.g. to query a server: `options` is then rendered as given */
onSearch?: (query: string) => void;
@@ -61,11 +58,10 @@
error?: boolean;
disabled?: boolean;
align?: SelectAlign;
- /** goes on the trigger, so a Field's label can point at it */
+ /** lets a Field's label point at this control */
id?: string;
class?: string;
panelClass?: string;
- /** pinned below the list, e.g. a "view all branches" link */
footer?: Snippet;
}
@@ -120,8 +116,6 @@
)
);
- // a heading per run of same-group options, so grouping falls out of the array's order
- // instead of needing a nested shape
const rows = $derived(
visible.map((option, index) => ({
option,
@@ -153,7 +147,6 @@
const move = (delta: number) => {
if (selectable.length === 0) return;
const current = selectable.indexOf(activeIndex);
- // from nowhere, ArrowDown lands on the first option and ArrowUp on the last
const next =
current === -1
? delta > 0
@@ -180,7 +173,6 @@
open = newState === "open";
if (!open) return;
- // every open starts from a clean slate, with the current selection under the cursor
search = "";
onSearch?.("");
const selectedIndex = options.findIndex((option) => option.value === value);
@@ -198,7 +190,6 @@
const onSearchInput = (event: Event) => {
search = (event.currentTarget as HTMLInputElement).value;
onSearch?.(search.trim());
- // the old active row is gone or has moved: start over at the top of what is left
setActive(selectable[0] ?? -1, true);
};
diff --git a/web/src/lib/components/ui/Dropdown.stories.svelte b/web/src/lib/components/ui/Dropdown.stories.svelte
index 019f1d70..2c535253 100644
--- a/web/src/lib/components/ui/Dropdown.stories.svelte
+++ b/web/src/lib/components/ui/Dropdown.stories.svelte
@@ -14,9 +14,7 @@
});
-
{#snippet menuTrigger()}
@@ -84,7 +82,6 @@
-
{#each ["small", "regular", "large"] as const as s (s)}
diff --git a/web/src/lib/components/ui/Link.stories.svelte b/web/src/lib/components/ui/Link.stories.svelte
index f4becbbe..95c3c7b8 100644
--- a/web/src/lib/components/ui/Link.stories.svelte
+++ b/web/src/lib/components/ui/Link.stories.svelte
@@ -27,7 +27,6 @@
});
-
Link
Confirmed
Careful
diff --git a/web/src/lib/components/ui/Radio.stories.svelte b/web/src/lib/components/ui/Radio.stories.svelte
index 52dfe7a5..1c2b85c0 100644
--- a/web/src/lib/components/ui/Radio.stories.svelte
+++ b/web/src/lib/components/ui/Radio.stories.svelte
@@ -19,7 +19,6 @@
});
-
Option
Option
Option
diff --git a/web/src/lib/components/ui/Select.svelte b/web/src/lib/components/ui/Select.svelte
index 1a848aad..ed75110b 100644
--- a/web/src/lib/components/ui/Select.svelte
+++ b/web/src/lib/components/ui/Select.svelte
@@ -16,19 +16,17 @@
* once a list is long enough that scanning it beats reading it. */
rich?: boolean;
onSelect?: (value: string) => void;
- /** empty-value row shown first, e.g. "Choose stat". Selectable, so it also clears. */
+ /** the empty row shown first, e.g. "Choose stat"; picking it clears the value */
placeholder?: string;
- /** names the control for screen readers */
label?: string;
- /** glyph pinned to the leading edge, mirroring the chevron. Decorative — it stands for
- * the current selection, so keep it in step with `value` rather than the list. */
+ /** the icon stands for the current selection, so keep it in step with `value`
+ * rather than the list. */
icon?: Component
;
error?: boolean;
disabled?: boolean;
- /** goes on the control itself, so a Field's label can point at it */
+ /** lets a Field's label point at this control */
id?: string;
class?: string;
- // rich only
searchPlaceholder?: string;
emptyLabel?: string;
/** take filtering over, e.g. to query a server: `options` is then rendered as given */
@@ -36,7 +34,6 @@
loading?: boolean;
align?: SelectAlign;
panelClass?: string;
- /** pinned below the list, e.g. a "view all branches" link */
footer?: Snippet;
}
@@ -61,7 +58,7 @@
footer
}: Props = $props();
- // a native optgroup has to nest, so the flat array is folded into runs of the same group.
+ // a native optgroup has to nest.
// the rich rendering keeps the array flat and emits headings between rows instead.
const groups = $derived(
options.reduce<{ label?: string; options: SelectOption[] }[]>((groups, option) => {
diff --git a/web/src/lib/components/ui/ShimmerText.svelte b/web/src/lib/components/ui/ShimmerText.svelte
index 307537c1..9b7d809b 100644
--- a/web/src/lib/components/ui/ShimmerText.svelte
+++ b/web/src/lib/components/ui/ShimmerText.svelte
@@ -2,12 +2,10 @@
import { tv, type VariantProps } from "tailwind-variants";
export const shimmerText = tv({
- // w-fit keeps the box hugging the glyphs inside a stretching flex or grid
- // parent, so the sweep is sized by the text rather than by the column
+ // the sweep is sized by the text rather than by the column
base: "inline-block w-fit",
variants: {
color: {
- /** keep whatever colour the surrounding text already has */
inherit: "",
default: "text-foreground-default",
muted: "text-foreground-muted",
@@ -70,7 +68,6 @@
from {
background-position-x: -100%;
}
- /* the sweep is done at the halfway mark; the rest of the cycle is a pause */
50%,
to {
background-position-x: 250%;
diff --git a/web/src/lib/components/ui/Tabs.stories.svelte b/web/src/lib/components/ui/Tabs.stories.svelte
index 11de46a3..32f72514 100644
--- a/web/src/lib/components/ui/Tabs.stories.svelte
+++ b/web/src/lib/components/ui/Tabs.stories.svelte
@@ -31,7 +31,7 @@
+ rather than hugging its items -->
{#snippet template(args)}
diff --git a/web/src/lib/components/ui/Tabs.svelte b/web/src/lib/components/ui/Tabs.svelte
index 6841cfff..f1858e7e 100644
--- a/web/src/lib/components/ui/Tabs.svelte
+++ b/web/src/lib/components/ui/Tabs.svelte
@@ -29,8 +29,7 @@
"rounded-sm border border-border-navigation-item-active bg-background-navigation-item",
item: "min-h-8 w-full gap-1.5 overflow-hidden rounded-sm border border-transparent px-3 py-1.5 typography-paragraph-regular text-foreground-default transition-colors duration-150 ease-in-out focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus",
icon: "size-3.5 shrink-0",
- // Figma clips the item rather than letting a long label wrap it taller; an
- // ellipsis keeps that height without hiding the label outright
+ // Figma clips the item rather than letting a long label wrap it taller
label: "min-w-0 truncate",
count: "ml-auto"
},
@@ -134,8 +133,6 @@
let bestLength = -1;
for (const item of items) {
if (item.url === dest.pathname + dest.search) return item.id;
- // a plain path also owns everything nested under it, so an issue page
- // keeps the issues tab lit; a query-scoped tab only matches exactly
if (item.url.includes("?")) continue;
const owns = dest.pathname === item.url || dest.pathname.startsWith(`${item.url}/`);
if (owns && item.url.length > bestLength) {
diff --git a/web/src/lib/components/ui/Tag.svelte b/web/src/lib/components/ui/Tag.svelte
index 7cd926cc..9327339b 100644
--- a/web/src/lib/components/ui/Tag.svelte
+++ b/web/src/lib/components/ui/Tag.svelte
@@ -37,7 +37,6 @@
interface Props extends Omit, "class" | "children"> {
color?: TagVariants["color"];
size?: TagVariants["size"];
- /** Optional leading icon component (e.g. `import Hash from "$icon/hash"`). */
icon?: Component;
class?: string;
iconClass?: string;
diff --git a/web/src/lib/components/ui/Textarea.svelte b/web/src/lib/components/ui/Textarea.svelte
index 7e942859..f8b6d33e 100644
--- a/web/src/lib/components/ui/Textarea.svelte
+++ b/web/src/lib/components/ui/Textarea.svelte
@@ -98,9 +98,8 @@
textareaField({ error, disabled, readonly, resizeable, size })
);
- // The root no longer carries the text inset (so the scrollbar/resize handle can sit flush
- // against the border) — the textarea itself owns its horizontal padding, collapsing to 0 on
- // whichever side an icon (or the loading spinner) already provides the inset instead.
+ // the root carries no text inset, so the scrollbar and the resize handle sit flush
+ // against the border.
const textareaPadding = $derived(
[iconLeft ? "" : "pl-2.5", loading ? "pr-[1.875rem]" : iconRight ? "" : "pr-2.5"]
.filter(Boolean)
diff --git a/web/src/lib/components/ui/Toggle.stories.svelte b/web/src/lib/components/ui/Toggle.stories.svelte
index 4bc86f15..f9831aed 100644
--- a/web/src/lib/components/ui/Toggle.stories.svelte
+++ b/web/src/lib/components/ui/Toggle.stories.svelte
@@ -19,7 +19,6 @@
});
-
Label
Label
Label
diff --git a/web/src/lib/components/ui/Toggle.svelte b/web/src/lib/components/ui/Toggle.svelte
index cf28efce..86b21aa7 100644
--- a/web/src/lib/components/ui/Toggle.svelte
+++ b/web/src/lib/components/ui/Toggle.svelte
@@ -5,7 +5,6 @@
interface Props extends Omit {
checked?: boolean;
disabled?: boolean;
- /** `inline` hugs the label, `block` stretches it to fill the row. */
layout?: "inline" | "block";
class?: string;
children?: Snippet;
diff --git a/web/src/lib/components/ui/Tooltip.stories.svelte b/web/src/lib/components/ui/Tooltip.stories.svelte
index fb43523c..02392278 100644
--- a/web/src/lib/components/ui/Tooltip.stories.svelte
+++ b/web/src/lib/components/ui/Tooltip.stories.svelte
@@ -11,8 +11,7 @@
});
-
@@ -43,7 +42,6 @@
-
@@ -52,7 +50,6 @@
-
@@ -61,7 +58,7 @@
-
@@ -75,7 +72,7 @@
+ even when the preferred side had to flip. -->
diff --git a/web/src/lib/components/ui/Tooltip.svelte b/web/src/lib/components/ui/Tooltip.svelte
index 743e440a..0cc99683 100644
--- a/web/src/lib/components/ui/Tooltip.svelte
+++ b/web/src/lib/components/ui/Tooltip.svelte
@@ -42,7 +42,6 @@
import { onMount } from "svelte";
interface Props {
- /** Tooltip body: a plain string for the common case, a snippet for rich content. */
content: string | Snippet;
side?: TooltipVariants["side"];
/** ms to wait before opening on hover. Keyboard focus always opens immediately. */
@@ -51,7 +50,6 @@
disabled?: boolean;
/** Two-way: set it to drive the tooltip yourself, e.g. a transient "Copied!". */
open?: boolean;
- /** Draw the little pointer at the trigger. */
arrow?: boolean;
id?: string;
class?: string;
diff --git a/web/src/lib/components/ui/selectField.ts b/web/src/lib/components/ui/selectField.ts
index ef75e1cf..2cafa91e 100644
--- a/web/src/lib/components/ui/selectField.ts
+++ b/web/src/lib/components/ui/selectField.ts
@@ -3,7 +3,7 @@ import { tv, type VariantProps } from "tailwind-variants";
/** one row of a Select, in either rendering */
export interface SelectOption {
value: string;
- /** shown instead of `value`; `value` is still matched when filtering */
+ /** `value` is still matched when filtering */
label?: string;
/** right-aligned secondary text, e.g. "default" on a repo's default branch. rich only:
* a native `` has nowhere to put it */
diff --git a/web/src/lib/components/welcome/FinishStep.svelte b/web/src/lib/components/welcome/FinishStep.svelte
index 904a900c..afd543ca 100644
--- a/web/src/lib/components/welcome/FinishStep.svelte
+++ b/web/src/lib/components/welcome/FinishStep.svelte
@@ -1,8 +1,7 @@