Skip to content

struts: a CSS and JS starter for web projects

by Kite Co.

A starting point you copy and tailor, not a library you install. Opinionated, and shaped by the sites we build.

Approach

Copy it, don't install it
Each project takes the parts it needs and changes them freely. There's no version to keep up with.
The platform first
<details>, <dialog>, commandfor and color-mix() do most of the work. JS fills the gaps, and everything works without it.
Tailwind underneath, used sparingly
Tokens and utilities come from Tailwind v4. Patterns are BEM blocks with a small custom-property API, so markup stays readable.
Colour by context
Set a surface and everything inside follows it. No per-component overrides.
Components in one folder
A component keeps its markup, styles and script together, with an example that the components page renders.
Written down
Each pattern's header comment is its API, and each choice has a short decision doc explaining why.

Get started

Give your coding agent this prompt from your project's root:

Adopt struts (https://github.com/kite-co-code/struts) into this project. Clone it to a temporary folder and follow its ADOPT.md. Plan the work in phases that each leave a working build, starting with the foundation.

ADOPT.md is the full procedure, for agents and people alike.

Colour

Surfaces set roles

Each palette colour gives a surface-* class: background, foreground, color-scheme and the role tokens it sets. Buttons, links, tags, rules and focus rings read the roles, so they follow the surface with no overrides.

On a colour

on-* sets the foreground and roles without a background, for text over an image or a background set some other way.

on-blue

Inline link example and a button

Dark scheme

Roles switch with the OS setting, or with data-scheme on any element. Kite Co.'s dark scheme is the blue. This box is always dark:

Text, an inline link example and buttons inside [data-scheme=dark].

Button Secondary

Type

Type and flow

This block is prose: flow spacing, a reading measure and list styling. Each element declares the space above it with --flow-space, so a paragraph after a heading sits closer than a heading after a paragraph.

A third-level heading

Followed straight away by a fourth

Headings are IBM Plex Serif, body is IBM Plex Sans, and labels are IBM Plex Mono. Sizes are fluid, built at compile time with fluid(min, max): resize the window to see them scale between the 400 and 1440 pixel frames.

  • List items get markers back inside prose
  • And spacing between them
    • Nested lists indent further
  1. Ordered lists use numbers
  2. Like this

Kite Co. brought both deep technical expertise and genuine care to every stage.

The client
  • not-prose
  • leaves
  • this list
  • alone

type-meta: captions, credits and dates.

type-h1 · Heading 1
Cultural organisations
type-h2 · Heading 2
Cultural organisations
type-h3 · Heading 3
Cultural organisations
type-h4 · Heading 4
Cultural organisations
type-base · Body
Better technology and better ways to communicate.
type-label · Surtitle
Announcement
type-label-sm · tag
CMS & Content Systems
type-meta · Caption
Visual identity by Doug Kerr

stack-8

One fixed gap between every child,

whatever the element.

flow

Each child sets its own space.

A figure falls back to layout space.

Layout

Tracks and roles

Children of layout-grid sit in the content track unless they ask for another. Kite Co.'s content runs the width of the frame, inset from the keyline, so text sits left.

layout-full
layout-wide
content (default)
layout-narrow
layout-start
layout-end

Outside the grid

layout-wide, not in a grid
layout-width-xs

Grids

Forms

If you think we'd be a good fit

Bare elements styled by the opt-in base/forms.css.

Budget

Behaviour

Native first

Disclosure

For a simple accordion, native <details name> needs no script:

First question

Only one in the group stays open.

Second question

Opening this closes the first.

Dialog

A dialog

Animate on scroll

Fades in

one after

another

No trigger, so it plays itself

Played by the box opposite, sliding up

Outer trigger’s item

Inner

trigger’s

items

Components

One folder each

Patterns style things that appear everywhere. A component is a whole piece of UI, like the header at the top of this page, with its markup, styles and behaviour kept together:

components/site-header/
  styles.css     @utility blocks; the header comment is the API
  scripts.ts     behaviour, registered with define()
  example.html   reference markup, one <template> per example

A project ports each example.html to whatever renders its pages, such as an Astro component or a PHP template. The components page shows every example in a frame you can resize.

Browse the components