Kern 0.12.0 GitHub
Fundamentals

Layout

Content has a maximum width, cards and tiles sit in a kern-grid, and breakpoints are measured on the container, not on the window.

Tokens

Widths for the page and the sidebar, and three breakpoints. Custom properties do not work in @media or @container conditions, so the breakpoint values are written literally there; the tokens are for documentation and for JavaScript (getComputedStyle(document.documentElement).getPropertyValue('--breakpoint-md')).

TokenValue
--content-max1280px
--content-wide1600px
--sidebar-width240px
--breakpoint-sm640px
--breakpoint-md960px
--breakpoint-lg1280px

Breakpoints

Kern components switch by the room they really have: kern-grid is a size container, and its columns and the spans of kern-grid-item change at 640, 960 and 1280 px of grid width. A grid in a sidebar, a drawer or half a page therefore behaves like on a small screen, without any extra configuration. Use the same values for your own container queries.

.panel { container-type: inline-size; } @container (min-width: 640px) { .panel .fields { grid-template-columns: 1fr 1fr; } }

Grid

kern-grid has two modes:

<kern-grid min="220px"> <kern-stat label="Devices online" value="36"></kern-stat> <kern-stat label="Updates" value="3"></kern-stat> </kern-grid> <kern-grid columns="12"> <kern-grid-item span="12" span-md="8"><kern-card heading="Devices">…</kern-card></kern-grid-item> <kern-grid-item span="12" span-md="4"><kern-card heading="Details">…</kern-card></kern-grid-item> </kern-grid>

Gaps come from the spacing scale (gap="sm|md|lg"); inside a card, use kern-grid too, not nested cards.

App shell

kern-app-shell is the frame around the pages: a topbar that stays at the top, the content with --content-max and page padding, and an optional footer. With a sidebar slot it adds a side navigation that stands on the left from 960 px and becomes a drawer below, opened by the menu button of kern-topbar. A skip link and the landmarks header, nav and main come with it.

<kern-app-shell> <kern-topbar slot="topbar" app-name="Shelly Manager"></kern-topbar> <div slot="sidebar">…</div> <!-- optional, see Navigation --> <kern-page-header heading="Devices"></kern-page-header> <kern-grid min="240px">…</kern-grid> </kern-app-shell>

Unlike the grid, the shell decides by its own width with a breakpoint property (default 960), because the side navigation is about the whole window, not one container.

Build the side navigation from kern-list variant="nav": one list per group with a heading, links as kern-list-item href with an icon in start and a counter as kern-badge mono in meta. current marks the open page (aria-current="page").

<div slot="sidebar"> <kern-list variant="nav" heading="Home"> <kern-list-item heading="Overview" href="/" current><kern-icon slot="start" name="home"></kern-icon></kern-list-item> <kern-list-item heading="Devices" href="/devices"> <kern-icon slot="start" name="power"></kern-icon> <kern-badge slot="meta" mono>38</kern-badge> </kern-list-item> </kern-list> <kern-list variant="nav" heading="System">…</kern-list> </div> document.addEventListener('kernNavigate', (e) => { e.preventDefault(); router.go(e.detail.href); if (e.target.localName === 'kern-list-item') { for (const el of document.querySelectorAll('kern-list-item[current]')) el.current = false; e.target.current = true; } document.querySelector('kern-app-shell').focusMain(); });