feat(ui): add decorative style primitives
This commit is contained in:
parent
975e17cd02
commit
51b97246a8
10 changed files with 476 additions and 0 deletions
97
src/lib/ui/components/CornerBracketFrame.svelte
Normal file
97
src/lib/ui/components/CornerBracketFrame.svelte
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
<script lang="ts">
|
||||||
|
let {
|
||||||
|
density = "regular",
|
||||||
|
size = "md",
|
||||||
|
tone = "neutral",
|
||||||
|
}: {
|
||||||
|
density?: "regular" | "tight";
|
||||||
|
size?: "sm" | "md" | "lg";
|
||||||
|
tone?: "neutral" | "accent" | "danger";
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="corner-bracket-frame"
|
||||||
|
data-density={density}
|
||||||
|
data-size={size}
|
||||||
|
data-tone={tone}
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<span data-corner="top-left"></span>
|
||||||
|
<span data-corner="top-right"></span>
|
||||||
|
<span data-corner="bottom-left"></span>
|
||||||
|
<span data-corner="bottom-right"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.corner-bracket-frame {
|
||||||
|
--corner-color: var(--ui-color-line-strong);
|
||||||
|
--corner-offset: 0.5rem;
|
||||||
|
--corner-size: 1.45rem;
|
||||||
|
position: relative;
|
||||||
|
min-height: 5rem;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background:
|
||||||
|
linear-gradient(90deg, color-mix(in srgb, var(--corner-color), transparent 78%) 1px, transparent 1px),
|
||||||
|
linear-gradient(color-mix(in srgb, var(--corner-color), transparent 84%) 1px, transparent 1px),
|
||||||
|
rgba(2, 3, 2, 0.64);
|
||||||
|
background-size: 2rem 2rem, 2rem 2rem, auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
position: absolute;
|
||||||
|
width: var(--corner-size);
|
||||||
|
height: var(--corner-size);
|
||||||
|
border-color: var(--corner-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-corner="top-left"] {
|
||||||
|
top: var(--corner-offset);
|
||||||
|
left: var(--corner-offset);
|
||||||
|
border-top: 1px solid;
|
||||||
|
border-left: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-corner="top-right"] {
|
||||||
|
top: var(--corner-offset);
|
||||||
|
right: var(--corner-offset);
|
||||||
|
border-top: 1px solid;
|
||||||
|
border-right: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-corner="bottom-left"] {
|
||||||
|
bottom: var(--corner-offset);
|
||||||
|
left: var(--corner-offset);
|
||||||
|
border-bottom: 1px solid;
|
||||||
|
border-left: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-corner="bottom-right"] {
|
||||||
|
right: var(--corner-offset);
|
||||||
|
bottom: var(--corner-offset);
|
||||||
|
border-right: 1px solid;
|
||||||
|
border-bottom: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.corner-bracket-frame[data-density="tight"] {
|
||||||
|
--corner-offset: 0.28rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.corner-bracket-frame[data-size="sm"] {
|
||||||
|
--corner-size: 0.95rem;
|
||||||
|
min-height: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.corner-bracket-frame[data-size="lg"] {
|
||||||
|
--corner-size: 2.25rem;
|
||||||
|
min-height: 8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.corner-bracket-frame[data-tone="accent"] {
|
||||||
|
--corner-color: var(--ui-color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.corner-bracket-frame[data-tone="danger"] {
|
||||||
|
--corner-color: var(--ui-color-danger);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
77
src/lib/ui/components/DiagonalStripeField.svelte
Normal file
77
src/lib/ui/components/DiagonalStripeField.svelte
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
<script lang="ts">
|
||||||
|
let {
|
||||||
|
density = "regular",
|
||||||
|
direction = "forward",
|
||||||
|
size = "md",
|
||||||
|
tone = "accent",
|
||||||
|
}: {
|
||||||
|
density?: "open" | "regular" | "tight";
|
||||||
|
direction?: "forward" | "backward";
|
||||||
|
size?: "sm" | "md" | "lg";
|
||||||
|
tone?: "neutral" | "accent" | "warning" | "danger";
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="diagonal-stripe-field"
|
||||||
|
data-density={density}
|
||||||
|
data-direction={direction}
|
||||||
|
data-size={size}
|
||||||
|
data-tone={tone}
|
||||||
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.diagonal-stripe-field {
|
||||||
|
--stripe-angle: 135deg;
|
||||||
|
--stripe-color: var(--ui-color-line-strong);
|
||||||
|
--stripe-gap: 1.1rem;
|
||||||
|
--stripe-width: 0.18rem;
|
||||||
|
display: block;
|
||||||
|
min-height: 3rem;
|
||||||
|
border: var(--ui-border);
|
||||||
|
background:
|
||||||
|
repeating-linear-gradient(
|
||||||
|
var(--stripe-angle),
|
||||||
|
transparent 0 var(--stripe-gap),
|
||||||
|
color-mix(in srgb, var(--stripe-color), transparent 26%) var(--stripe-gap)
|
||||||
|
calc(var(--stripe-gap) + var(--stripe-width))
|
||||||
|
),
|
||||||
|
linear-gradient(90deg, rgba(255, 255, 255, 0.05), transparent 42%),
|
||||||
|
rgba(2, 3, 2, 0.82);
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagonal-stripe-field[data-direction="backward"] {
|
||||||
|
--stripe-angle: 45deg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagonal-stripe-field[data-density="open"] {
|
||||||
|
--stripe-gap: 1.65rem;
|
||||||
|
--stripe-width: 0.16rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagonal-stripe-field[data-density="tight"] {
|
||||||
|
--stripe-gap: 0.72rem;
|
||||||
|
--stripe-width: 0.14rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagonal-stripe-field[data-size="sm"] {
|
||||||
|
min-height: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagonal-stripe-field[data-size="lg"] {
|
||||||
|
min-height: 5.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagonal-stripe-field[data-tone="accent"] {
|
||||||
|
--stripe-color: var(--ui-color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagonal-stripe-field[data-tone="warning"] {
|
||||||
|
--stripe-color: var(--ui-color-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagonal-stripe-field[data-tone="danger"] {
|
||||||
|
--stripe-color: var(--ui-color-danger);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
66
src/lib/ui/components/ScanlineField.svelte
Normal file
66
src/lib/ui/components/ScanlineField.svelte
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
<script lang="ts">
|
||||||
|
let {
|
||||||
|
intensity = "medium",
|
||||||
|
size = "md",
|
||||||
|
tone = "neutral",
|
||||||
|
}: {
|
||||||
|
intensity?: "soft" | "medium" | "hard";
|
||||||
|
size?: "sm" | "md" | "lg";
|
||||||
|
tone?: "neutral" | "accent" | "warning";
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="scanline-field"
|
||||||
|
data-intensity={intensity}
|
||||||
|
data-size={size}
|
||||||
|
data-tone={tone}
|
||||||
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.scanline-field {
|
||||||
|
--scan-color: rgba(243, 244, 237, 0.12);
|
||||||
|
--scan-gap: 0.48rem;
|
||||||
|
--scan-weight: 1px;
|
||||||
|
display: block;
|
||||||
|
min-height: 4rem;
|
||||||
|
border: var(--ui-border);
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 18% 24%, color-mix(in srgb, var(--scan-color), transparent 35%) 0 1px, transparent 2px),
|
||||||
|
radial-gradient(circle at 76% 62%, color-mix(in srgb, var(--scan-color), transparent 44%) 0 1px, transparent 2px),
|
||||||
|
repeating-linear-gradient(
|
||||||
|
180deg,
|
||||||
|
transparent 0 calc(var(--scan-gap) - var(--scan-weight)),
|
||||||
|
var(--scan-color) calc(var(--scan-gap) - var(--scan-weight)) var(--scan-gap)
|
||||||
|
),
|
||||||
|
rgba(6, 7, 6, 0.78);
|
||||||
|
background-size: 3.1rem 3.1rem, 4.7rem 4.7rem, auto, auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scanline-field[data-intensity="soft"] {
|
||||||
|
--scan-color: rgba(243, 244, 237, 0.07);
|
||||||
|
--scan-gap: 0.62rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scanline-field[data-intensity="hard"] {
|
||||||
|
--scan-color: rgba(215, 255, 0, 0.2);
|
||||||
|
--scan-gap: 0.36rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scanline-field[data-size="sm"] {
|
||||||
|
min-height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scanline-field[data-size="lg"] {
|
||||||
|
min-height: 6.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scanline-field[data-tone="accent"] {
|
||||||
|
--scan-color: rgba(215, 255, 0, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scanline-field[data-tone="warning"] {
|
||||||
|
--scan-color: rgba(255, 176, 32, 0.16);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
112
src/lib/ui/components/SignalTrace.svelte
Normal file
112
src/lib/ui/components/SignalTrace.svelte
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
<script lang="ts">
|
||||||
|
let {
|
||||||
|
density = "regular",
|
||||||
|
orientation = "horizontal",
|
||||||
|
tone = "accent",
|
||||||
|
}: {
|
||||||
|
density?: "regular" | "tight";
|
||||||
|
orientation?: "horizontal" | "vertical";
|
||||||
|
tone?: "neutral" | "accent" | "warning";
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="signal-trace"
|
||||||
|
data-density={density}
|
||||||
|
data-orientation={orientation}
|
||||||
|
data-tone={tone}
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<span class="signal-trace__rail"></span>
|
||||||
|
<span class="signal-trace__node" data-node="start"></span>
|
||||||
|
<span class="signal-trace__node" data-node="middle"></span>
|
||||||
|
<span class="signal-trace__node" data-node="end"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.signal-trace {
|
||||||
|
--trace-color: var(--ui-color-accent);
|
||||||
|
--trace-track: min(100%, 20rem);
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace__rail {
|
||||||
|
position: absolute;
|
||||||
|
inset: 50% 0 auto;
|
||||||
|
height: 1px;
|
||||||
|
background:
|
||||||
|
linear-gradient(90deg, transparent, var(--trace-color) 18%, var(--trace-color) 82%, transparent),
|
||||||
|
linear-gradient(90deg, transparent 0 35%, rgba(255, 255, 255, 0.42) 35% 38%, transparent 38%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace__node {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
width: 0.55rem;
|
||||||
|
height: 0.55rem;
|
||||||
|
border: 1px solid var(--trace-color);
|
||||||
|
background: var(--ui-color-canvas);
|
||||||
|
transform: translate(-50%, -50%) rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace__node[data-node="start"] {
|
||||||
|
left: 18%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace__node[data-node="middle"] {
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace__node[data-node="end"] {
|
||||||
|
left: 82%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace[data-density="tight"] {
|
||||||
|
min-height: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace[data-density="tight"] .signal-trace__node {
|
||||||
|
width: 0.38rem;
|
||||||
|
height: 0.38rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace[data-orientation="vertical"] {
|
||||||
|
width: 1.5rem;
|
||||||
|
min-height: 12rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace[data-orientation="vertical"] .signal-trace__rail {
|
||||||
|
inset: 0 auto 0 50%;
|
||||||
|
width: 1px;
|
||||||
|
height: auto;
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, transparent, var(--trace-color) 18%, var(--trace-color) 82%, transparent),
|
||||||
|
linear-gradient(180deg, transparent 0 35%, rgba(255, 255, 255, 0.42) 35% 38%, transparent 38%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace[data-orientation="vertical"] .signal-trace__node[data-node="start"] {
|
||||||
|
top: 18%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace[data-orientation="vertical"] .signal-trace__node[data-node="middle"] {
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace[data-orientation="vertical"] .signal-trace__node[data-node="end"] {
|
||||||
|
top: 82%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace[data-tone="neutral"] {
|
||||||
|
--trace-color: var(--ui-color-line-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-trace[data-tone="warning"] {
|
||||||
|
--trace-color: var(--ui-color-warning);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
export { default as Badge } from "./components/Badge.svelte";
|
export { default as Badge } from "./components/Badge.svelte";
|
||||||
export { default as Button } from "./components/Button.svelte";
|
export { default as Button } from "./components/Button.svelte";
|
||||||
|
export { default as CornerBracketFrame } from "./components/CornerBracketFrame.svelte";
|
||||||
export { default as DashboardHeader } from "./components/DashboardHeader.svelte";
|
export { default as DashboardHeader } from "./components/DashboardHeader.svelte";
|
||||||
export { default as DashboardFrame } from "./components/DashboardFrame.svelte";
|
export { default as DashboardFrame } from "./components/DashboardFrame.svelte";
|
||||||
|
export { default as DiagonalStripeField } from "./components/DiagonalStripeField.svelte";
|
||||||
export { default as FooterCell } from "./components/FooterCell.svelte";
|
export { default as FooterCell } from "./components/FooterCell.svelte";
|
||||||
export { default as FooterStatusCell } from "./components/FooterStatusCell.svelte";
|
export { default as FooterStatusCell } from "./components/FooterStatusCell.svelte";
|
||||||
export { default as GridFrame } from "./components/GridFrame.svelte";
|
export { default as GridFrame } from "./components/GridFrame.svelte";
|
||||||
|
|
@ -14,6 +16,8 @@ export { default as Separator } from "./components/Separator.svelte";
|
||||||
export { default as ServiceGroupPanel } from "./components/ServiceGroupPanel.svelte";
|
export { default as ServiceGroupPanel } from "./components/ServiceGroupPanel.svelte";
|
||||||
export { default as ServicePanel } from "./components/ServicePanel.svelte";
|
export { default as ServicePanel } from "./components/ServicePanel.svelte";
|
||||||
export { default as ServiceRow } from "./components/ServiceRow.svelte";
|
export { default as ServiceRow } from "./components/ServiceRow.svelte";
|
||||||
|
export { default as ScanlineField } from "./components/ScanlineField.svelte";
|
||||||
|
export { default as SignalTrace } from "./components/SignalTrace.svelte";
|
||||||
export { default as Sparkline } from "./components/Sparkline.svelte";
|
export { default as Sparkline } from "./components/Sparkline.svelte";
|
||||||
export { default as StatusBadge } from "./components/StatusBadge.svelte";
|
export { default as StatusBadge } from "./components/StatusBadge.svelte";
|
||||||
export { default as StatusStrip } from "./components/StatusStrip.svelte";
|
export { default as StatusStrip } from "./components/StatusStrip.svelte";
|
||||||
|
|
|
||||||
22
src/lib/ui/stories/CornerBracketFrame.stories.svelte
Normal file
22
src/lib/ui/stories/CornerBracketFrame.stories.svelte
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
<script module lang="ts">
|
||||||
|
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||||
|
import CornerBracketFrame from "../components/CornerBracketFrame.svelte";
|
||||||
|
import GridFrame from "../components/GridFrame.svelte";
|
||||||
|
|
||||||
|
const { Story } = defineMeta({
|
||||||
|
title: "Style Primitives/CornerBracketFrame",
|
||||||
|
component: CornerBracketFrame,
|
||||||
|
tags: ["autodocs"],
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Story name="Neutral Medium" args={{ tone: "neutral", size: "md" }} />
|
||||||
|
<Story name="Accent Tight" args={{ tone: "accent", density: "tight" }} />
|
||||||
|
<Story name="Large Danger" args={{ tone: "danger", size: "lg" }} />
|
||||||
|
<Story name="Grid Set" asChild>
|
||||||
|
<GridFrame density="compact">
|
||||||
|
<CornerBracketFrame tone="neutral" size="sm" />
|
||||||
|
<CornerBracketFrame tone="accent" size="sm" />
|
||||||
|
<CornerBracketFrame tone="danger" size="sm" density="tight" />
|
||||||
|
</GridFrame>
|
||||||
|
</Story>
|
||||||
23
src/lib/ui/stories/DiagonalStripeField.stories.svelte
Normal file
23
src/lib/ui/stories/DiagonalStripeField.stories.svelte
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<script module lang="ts">
|
||||||
|
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||||
|
import DiagonalStripeField from "../components/DiagonalStripeField.svelte";
|
||||||
|
import Panel from "../components/Panel.svelte";
|
||||||
|
import SignalTrace from "../components/SignalTrace.svelte";
|
||||||
|
|
||||||
|
const { Story } = defineMeta({
|
||||||
|
title: "Style Primitives/DiagonalStripeField",
|
||||||
|
component: DiagonalStripeField,
|
||||||
|
tags: ["autodocs"],
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Story name="Accent Regular" args={{ density: "regular", tone: "accent", size: "md" }} />
|
||||||
|
<Story name="Tight Warning" args={{ density: "tight", tone: "warning", direction: "backward" }} />
|
||||||
|
<Story name="Large Danger" args={{ density: "open", tone: "danger", size: "lg" }} />
|
||||||
|
<Story name="Composed Divider" asChild>
|
||||||
|
<Panel title="Generic Surface">
|
||||||
|
<DiagonalStripeField density="tight" size="sm" />
|
||||||
|
<SignalTrace tone="accent" />
|
||||||
|
<DiagonalStripeField direction="backward" tone="neutral" />
|
||||||
|
</Panel>
|
||||||
|
</Story>
|
||||||
20
src/lib/ui/stories/ScanlineField.stories.svelte
Normal file
20
src/lib/ui/stories/ScanlineField.stories.svelte
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<script module lang="ts">
|
||||||
|
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||||
|
import ScanlineField from "../components/ScanlineField.svelte";
|
||||||
|
import Panel from "../components/Panel.svelte";
|
||||||
|
|
||||||
|
const { Story } = defineMeta({
|
||||||
|
title: "Style Primitives/ScanlineField",
|
||||||
|
component: ScanlineField,
|
||||||
|
tags: ["autodocs"],
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Story name="Soft Neutral" args={{ intensity: "soft", tone: "neutral", size: "md" }} />
|
||||||
|
<Story name="Medium Accent" args={{ intensity: "medium", tone: "accent", size: "md" }} />
|
||||||
|
<Story name="Hard Warning" args={{ intensity: "hard", tone: "warning", size: "lg" }} />
|
||||||
|
<Story name="Panel Backdrop" asChild>
|
||||||
|
<Panel title="Signal Surface">
|
||||||
|
<ScanlineField intensity="hard" tone="accent" />
|
||||||
|
</Panel>
|
||||||
|
</Story>
|
||||||
21
src/lib/ui/stories/SignalTrace.stories.svelte
Normal file
21
src/lib/ui/stories/SignalTrace.stories.svelte
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script module lang="ts">
|
||||||
|
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||||
|
import SignalTrace from "../components/SignalTrace.svelte";
|
||||||
|
import Panel from "../components/Panel.svelte";
|
||||||
|
|
||||||
|
const { Story } = defineMeta({
|
||||||
|
title: "Style Primitives/SignalTrace",
|
||||||
|
component: SignalTrace,
|
||||||
|
tags: ["autodocs"],
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Story name="Horizontal Accent" args={{ tone: "accent", orientation: "horizontal" }} />
|
||||||
|
<Story name="Horizontal Warning Tight" args={{ tone: "warning", density: "tight" }} />
|
||||||
|
<Story name="Vertical Neutral" args={{ tone: "neutral", orientation: "vertical" }} />
|
||||||
|
<Story name="Panel Trace" asChild>
|
||||||
|
<Panel title="Generic Flow">
|
||||||
|
<SignalTrace tone="accent" />
|
||||||
|
<SignalTrace tone="warning" density="tight" />
|
||||||
|
</Panel>
|
||||||
|
</Story>
|
||||||
|
|
@ -12,6 +12,8 @@ const requiredStoryFiles = [
|
||||||
"DashboardFrame.stories.svelte",
|
"DashboardFrame.stories.svelte",
|
||||||
"DashboardHeader.stories.svelte",
|
"DashboardHeader.stories.svelte",
|
||||||
"DashboardOnePager.stories.svelte",
|
"DashboardOnePager.stories.svelte",
|
||||||
|
"CornerBracketFrame.stories.svelte",
|
||||||
|
"DiagonalStripeField.stories.svelte",
|
||||||
"FooterCell.stories.svelte",
|
"FooterCell.stories.svelte",
|
||||||
"FooterStatusCell.stories.svelte",
|
"FooterStatusCell.stories.svelte",
|
||||||
"GridFrame.stories.svelte",
|
"GridFrame.stories.svelte",
|
||||||
|
|
@ -24,10 +26,12 @@ const requiredStoryFiles = [
|
||||||
"ServiceGroupPanel.stories.svelte",
|
"ServiceGroupPanel.stories.svelte",
|
||||||
"ServicePanel.stories.svelte",
|
"ServicePanel.stories.svelte",
|
||||||
"ServiceRow.stories.svelte",
|
"ServiceRow.stories.svelte",
|
||||||
|
"SignalTrace.stories.svelte",
|
||||||
"Sparkline.stories.svelte",
|
"Sparkline.stories.svelte",
|
||||||
"StatusBadge.stories.svelte",
|
"StatusBadge.stories.svelte",
|
||||||
"StatusStrip.stories.svelte",
|
"StatusStrip.stories.svelte",
|
||||||
"SystemState.stories.svelte",
|
"SystemState.stories.svelte",
|
||||||
|
"ScanlineField.stories.svelte",
|
||||||
"TelemetryCard.stories.svelte",
|
"TelemetryCard.stories.svelte",
|
||||||
"TelemetryGrid.stories.svelte",
|
"TelemetryGrid.stories.svelte",
|
||||||
"TelemetryStrip.stories.svelte",
|
"TelemetryStrip.stories.svelte",
|
||||||
|
|
@ -79,6 +83,36 @@ describe("Storybook inventory", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("includes style-only decorative primitives in the reusable UI inventory", () => {
|
||||||
|
for (const component of [
|
||||||
|
"CornerBracketFrame",
|
||||||
|
"DiagonalStripeField",
|
||||||
|
"ScanlineField",
|
||||||
|
"SignalTrace",
|
||||||
|
]) {
|
||||||
|
expect(existsSync(join(componentsDir, `${component}.svelte`)), `${component} is missing`).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
existsSync(join(storiesDir, `${component}.stories.svelte`)),
|
||||||
|
`${component}.stories.svelte is missing`,
|
||||||
|
).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("keeps decorative primitives hidden from assistive technology", () => {
|
||||||
|
for (const component of [
|
||||||
|
"CornerBracketFrame",
|
||||||
|
"DiagonalStripeField",
|
||||||
|
"ScanlineField",
|
||||||
|
"SignalTrace",
|
||||||
|
]) {
|
||||||
|
const source = readFileSync(join(componentsDir, `${component}.svelte`), "utf8");
|
||||||
|
|
||||||
|
expect(source).toContain('aria-hidden="true"');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test("does not add deferred form/navigation primitives", () => {
|
test("does not add deferred form/navigation primitives", () => {
|
||||||
for (const component of ["Input", "ToggleGroup", "ScrollArea"]) {
|
for (const component of ["Input", "ToggleGroup", "ScrollArea"]) {
|
||||||
expect(existsSync(join(root, `src/lib/ui/components/${component}.svelte`))).toBe(false);
|
expect(existsSync(join(root, `src/lib/ui/components/${component}.svelte`))).toBe(false);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue