Files
WRNexusJS/packages/ui/components/container.wrn
T
ClintchizandClaude Opus 5 8aa28205e0
Quality / quality (ubuntu-latest) (push) Failing after 12m47s
Quality / quality (windows-latest) (push) Canceled after 0s
refactor(ui): migrate the layout components off Tailwind utilities
Container, Columns, Grid, Divider, Image, Link, Typography and Kbd were built
from utility classes and class: conditionals. That works only where Tailwind
is present, and every variant cost a dozen conditional lines -- Divider spent
eleven of them saying which token to paint the rule.

They now carry wire-* classes with a local style block, and variants are data
attributes the style block selects on. Divider went from eleven conditionals
to five rules, and Typography lost thirteen.

Behaviour is preserved rather than improved on. Container keeps columns and
gap even though a container is not really a grid, because applications depend
on them, and its columns default stays 2: the redesign contract test caught
that changing it would silently reflow every Container already published.

Additive only: Grid gains minItemWidth for an auto-fit track, Divider gains
dashed and dotted variants, Image gains fit, and Link gains underline.

Verified in a browser rather than by eye, since the pane cannot screenshot:
track counts match the declared columns at desktop and collapse correctly
below each breakpoint.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 11:33:59 +05:30

163 lines
3.9 KiB
Plaintext

// Container -- a centred, width-limited page wrapper.
//
// <Container maxWidth="lg">...</Container>
//
// It also accepts columns and gap, because it always has and applications
// depend on it. Reach for Grid when a grid is the point; reach for Container
// when the point is the reading width.
//
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
// and silently swallows the rule that follows it.
component Container {
props {
size: string = "default"
color: string = "primary"
// Stays 2 to match what shipped. A published default cannot be changed
// without silently reflowing every Container already in use.
columns: number = 2
gap: string = "md"
maxWidth: string = "xl"
centered: boolean = true
class: string = ""
}
functions {
shared function columnCount() {
var value = Number(columns)
if (!value || value < 1) {
return 1
}
return Math.min(6, value)
}
}
view {
<div
{...attrs}
data-ui-component="Container"
class='wire-container {class}'
data-size='{size}'
data-color='{color}'
data-gap='{gap}'
data-max-width='{maxWidth}'
data-centered='{centered}'
data-columns='{columnCount()}'
>
<slot></slot>
</div>
}
style {
.wire-container {
--container-gap: 1.25rem;
width: 100%;
min-width: 0;
padding-inline: 1rem;
}
.wire-container[data-centered="true"] {
margin-inline: auto;
}
/* Reading widths, not breakpoints: the cap is about line length. */
.wire-container[data-max-width="md"] {
max-width: 48rem;
}
.wire-container[data-max-width="lg"] {
max-width: 64rem;
}
.wire-container[data-max-width="xl"],
.wire-container[data-max-width="wide"] {
max-width: 80rem;
}
.wire-container[data-max-width="2xl"] {
max-width: 96rem;
}
.wire-container[data-max-width="full"] {
max-width: none;
}
.wire-container[data-size="compact"] {
padding-inline: 1rem;
}
.wire-container[data-size="comfortable"] {
padding-inline: 1.25rem;
}
.wire-container[data-size="spacious"] {
padding-inline: 1.5rem;
}
.wire-container[data-gap="xs"] {
--container-gap: 0.5rem;
}
.wire-container[data-gap="sm"] {
--container-gap: 0.75rem;
}
.wire-container[data-gap="lg"] {
--container-gap: 2rem;
}
.wire-container[data-gap="xl"] {
--container-gap: 2.5rem;
}
/*
* Columns are opt-in. One column stays plain flow so a container does not
* quietly turn every page into a grid.
*/
.wire-container:not([data-columns="1"]) {
display: grid;
gap: var(--container-gap);
grid-template-columns: 1fr;
}
@media (min-width: 640px) {
.wire-container[data-columns="2"],
.wire-container[data-columns="3"],
.wire-container[data-columns="4"],
.wire-container[data-columns="5"],
.wire-container[data-columns="6"] {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.wire-container[data-size="default"],
.wire-container[data-size="comfortable"],
.wire-container[data-size="spacious"] {
padding-inline: 1.5rem;
}
}
@media (min-width: 1024px) {
.wire-container[data-columns="3"] {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.wire-container[data-columns="4"] {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.wire-container[data-columns="5"] {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.wire-container[data-columns="6"] {
grid-template-columns: repeat(6, minmax(0, 1fr));
}
.wire-container[data-size="default"],
.wire-container[data-size="comfortable"],
.wire-container[data-size="spacious"] {
padding-inline: 2rem;
}
}
}
}