feat(ui): add decorative style primitives

This commit is contained in:
vince 2026-06-18 20:07:03 +02:00
parent 975e17cd02
commit 51b97246a8
10 changed files with 476 additions and 0 deletions

View 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>

View 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>

View 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>

View 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>