AniK UI Playground

Plain HTML and a small demo.css for presentation only. Run npm run build first so ../dist/ exists. The badge in the corner shows the active viewport breakpoint; resize the window to watch it change.

Row & columns (nested, >12 children)

12 children, no column class

1
2
3
4
5
6
7
8
9
10
11
12

Children without a column class take one track each (auto placement).

Spans

ak-col-8
ak-col-4
ak-col-4
ak-col-4
ak-col-4
ak-col-3
ak-col-6
auto
auto
auto

More than 12 columns of children — wraps to a new line

1
2
3
4
5 (col 13+)
6
7 (ak-col-6)

Seven children spanning 24 columns: the highlighted ones start a new row, no ak-row wrapper per line needed.

Nested rows

ak-col-8 containing its own 12-column row
4
4
4
ak-col-4

Flush grid — ak-gap-0

no gutter
no gutter
no gutter

Wide content cannot blow out a track

const wide = "a <pre> much wider than its column scrolls inside it";
ak-col-6 keeps its half

minmax(0, 1fr) tracks plus min-width: 0 on every child: the <pre> scrolls instead of widening the column.

Page wrapper & layout tokens

ak-page caps width at --ak-page-max (75rem) with --ak-page-gutter side padding. Both are plain custom properties, overridable per element (D-041).

ak-page with --ak-page-max: 30rem
--ak-page-max: none — full width, gutters kept (no ak-page-fluid class)
--ak-row-gap: var(--ak-space-lg)
on this row only

Mind the width: a 12-track row always has 11 gaps, even when every child spans all 12. At 3xl (64px) the row needs 704px before any content, so wide gaps belong behind a breakpoint — see the escape hatch under Spacing.

Responsive layout (viewport)

Mobile-first: the unprefixed class applies everywhere, each -sm/-md/-lg/-xl variant takes over from its breakpoint up. Resize the window.

Cards: 1 → 2 (sm) → 4 (lg) per line

Card 1
Card 2
Card 3
Card 4

ak-col-12 ak-col-sm-6 ak-col-lg-3

Main + aside: stacked, side by side from md

ak-col-12 ak-col-md-8

Flex direction: column, row from md

one
two
three

ak-flex ak-dir-column ak-dir-md-row

Container queries (ak-cq + ak-row together)

Drag the bottom-end corner of the blue frame. The layout follows the width of the ak-cq element, not the window. Thresholds are their own scale: c-sm 320, c-md 480, c-lg 640, c-xl 800px (D-023).

ak-cq and ak-row on the same element

A
B
C
D

Children use ak-col-12 ak-col-c-md-6 ak-col-c-lg-3. The row's children query the row itself, because a container query looks at the nearest ancestor container.

Same component, two containers

Identical markup in a narrow and a wide column. On a wide window the card stacks in the narrow column and goes horizontal in the wide one — same viewport, different containers. Below md both columns are full width, so both cards follow the page width.

media
Card title

ak-dir-column ak-dir-c-md-row

media
Card title

ak-dir-column ak-dir-c-md-row

Container variants beat viewport variants

ak-col-lg-6 ak-col-c-lg-4

On a large viewport, narrow the frame below 640px and the box goes to half width (the viewport variant); widen it past 640px and the container variant wins with a third. @container blocks are emitted after every @media block, so at equal specificity the container variant takes precedence.

ak-fixed inside ak-cq — containment caveat

The original container-query specification made an ak-cq element the containing block for position: fixed descendants. Browsers at the support floor (Chrome 111, Firefox 113, Safari 16.4) still do; current ones do not (D-048). This frame is a live test: it shows what your browser does, next to a contain: layout control that always captures.

Spacing & the --ak-row-gap escape hatch

Scale: ak-p-*

0
2xs
xs
sm
md
lg
xl
2xl
3xl

2, 4, 8, 16, 24, 32, 48, 64px — all through --ak-space-*.

Per-side and axis utilities

ak-pt-xl
ak-px-2xl
ak-py-lg
ak-ps-3xl

Gap: independent axes

x: xl
y: 2xs
x: xl
y: 2xs
x: xl
y: 2xs
x: xl
y: 2xs
x: xl
y: 2xs
x: xl
y: 2xs

ak-flex ak-flex-wrap ak-gap-x-xl ak-gap-y-2xs

Auto margins: ak-ms-auto pushes to the end

Logo
Nav
ak-ms-auto

Responsive spacing escape hatch

Spacing utilities have no breakpoint variants by design. The row below has no gap class at all; the consumer CSS in demo.css moves --ak-row-gap from 2xs to 2xl at md:

.demo-card-grid { --ak-row-gap: var(--ak-space-2xs); }
@media (min-width: 768px) {
  .demo-card-grid { --ak-row-gap: var(--ak-space-2xl); }
}
1
2
3
4

Flex — fixed sidebar + filling main (ak-flex-1)

ak-flex-1 ak-min-w-0 — takes all remaining space

Grow, shrink and auto

ak-grow
ak-grow-0
ak-flex-auto with a longer label
ak-shrink-0 stays wide
ak-shrink

Wrap vs. nowrap

wrapwrapwrapwrapwrapwrapwrapwrapwrapwrapwrapwrap
nowrapnowrapnowrapnowrapnowrapnowrapnowrapnowrapnowrapnowrapnowrapnowrap

Inline flex

Text with an ● ak-inline-flex chip in the middle of a sentence.

Alignment

ak-items-* (cross axis)

start A
center A
end A
stretch A
baseline A

ak-justify-* (main axis)

start·
center·
end·
between··

ak-self-* per item

self-start self-center self-end self-stretch self-auto

The same classes on ak-row (grid)

ak-row ak-items-center
ak-self-end
ak-self-stretch

The values are start/end, not flex-start/flex-end, so they work in grid and flex alike.

Typography

Text scale with paired line-heights

ak-text-xs The quick brown fox

ak-text-sm The quick brown fox

ak-text-md The quick brown fox

ak-text-lg The quick brown fox

ak-text-xl The quick brown fox

ak-text-2xl The quick brown fox

Weight and alignment

ak-font-bold Bold text

ak-font-normal on a heading

ak-text-start

ak-text-center

ak-text-end

Display type, tracking & prose (D-043)

Fluid display sizes — resize the window

3xl — 36 → 40px

4xl — 40 → 48px

5xl — 44 → 60px

6xl — 48 → 72px

Tracking

ak-tracking-tight

ak-tracking-normal

ak-tracking-wide — for small caps and labels

ak-measure + ak-flow

A readable column

ak-measure caps the line length at 65 characters, where long-form text is comfortable to read. ak-flow puts the same space between each direct child, whatever the elements are.

The flow space is 1em, resolved on each child, so it scales with the text it follows.

This paragraph has ak-mt-2xl: spacing utilities override the rhythm, because ak-flow is emitted before them.

ak-section — fluid block padding (64 → 120px)

ak-section
ak-section ak-pt-0 — a padding utility wins

Positioning

Corner badge

ak-relative ak-absolute ak-top-0 ak-end-0

Overlay

ak-relative
ak-absolute ak-inset-0

Sticky header

ak-sticky ak-top-0

Scroll this box.

The header stays at the top of it.

Line

Line

Line

Line

Line

Start / bottom offsets

ak-absolute ak-bottom-0 ak-start-0

Offsets are logical: ak-start-0 is the left edge in LTR and the right edge in RTL (see the RTL section).

Visibility — hide at one size, restore at another

Resize the window and watch which boxes exist. Each box names the viewport range it is visible in.

ak-hidden-md
visible below md only
ak-hidden ak-block-md
visible from md up
ak-hidden-sm ak-block-lg
hidden sm–lg, restored at lg
ak-hidden ak-flex-md ak-hidden-xl — flex from md to xl only

By container width

ak-hidden-c-md — narrow containers only
ak-hidden ak-inline-block-c-md — from c-md up

Display utilities

ak-block on a span ak-inline-block ak-inline-block ak-inline

ak-sr-only

Visually hidden, still in the accessibility tree. Open the browser's accessibility inspector, or use a screen reader, to check that these controls have names.

The button's accessible name is “Close dialog”; the search field's is “Search the docs”.

Sizing

ak-w-full / ak-w-fit

ak-w-full — fills the inline axis.

ak-w-fit — shrinks to content, capped by the parent.

ak-min-w-0 — the flex overflow fix

Both rows hold the same wide <pre>. A flex child's automatic minimum size is its min-content width, so without ak-min-w-0 the main region refuses to shrink and the row overflows (the dashed frame scrolls to show it). With it, the main region takes its share and the <pre> scrolls. ak-row does this for its children automatically.

Without ak-min-w-0 — the row overflows:

const config = { breakpoints: { sm: "576px", md: "768px", lg: "992px", xl: "1200px" }, containerBreakpoints: { "c-sm": "320px", "c-md": "480px", "c-lg": "640px", "c-xl": "800px" } };

With ak-min-w-0 — the pre scrolls:

const config = { breakpoints: { sm: "576px", md: "768px", lg: "992px", xl: "1200px" }, containerBreakpoints: { "c-sm": "320px", "c-md": "480px", "c-lg": "640px", "c-xl": "800px" } };

ak-h-full inside a sized parent

ak-h-full

ak-min-h-screen vs ak-h-screen

Both read --ak-viewport-block (default 100dvb). The min- variant is the one you usually want: it fills the viewport but still grows for taller content instead of clipping it. Resize / scroll on a phone to see dvb track the browser chrome.

ak-min-h-screen — fills the viewport, grows if the content is taller.

RTL — logical properties flip

The same markup twice, the second inside dir="rtl". Every inline-axis utility is logical, so start/end swap sides with no extra classes.

dir="ltr"

start ak-ps-3xl ak-text-start
first ak-ms-auto
col-8
col-4

dir="rtl"

start ak-ps-3xl ak-text-start
first ak-ms-auto
col-8
col-4

Reset on vs. off

The reset is global, so each side is its own document. The two frames load the same markup; only the left one loads anik-reset.css. Look at the body margin (dashed outline), headings, the long word, role="list", the dd indent, the fixed-width box, the button font and the SVG.

Reset on

Reset off