A lightweight, utility-first CSS framework for building modern interfaces
Get up and running with Goku CSS in minutes. Choose your preferred installation method:
npm install goku-css
Import the framework in your project:
// Everything, as source files — your bundler inlines the imports
import 'goku-css'
// Pre-bundled and minified, if you'd rather skip that step
import 'goku-css/bundle'
// Or cherry-pick only what you need
import 'goku-css/css/variables.css'
import 'goku-css/css/elements/button.css'
For quick prototyping, drop in the pre-bundled build — one request, no import waterfall:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/goku-css">
<!-- or pin a version -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/goku-css@8/docs/index.css">
Goku CSS is built with CSS custom properties, making it incredibly easy to customize. Change colors, spacing, typography, and more with simple variable overrides.
document.documentElement.style.setProperty('--g-primary', '#your-color')
:root {
color-scheme: light dark;
--g-font-family: system-ui, sans-serif;
--g-font-weight: 400;
--g-font-size: 1rem;
--g-line-height: 1.5;
--g-primary: light-dark(#171a18, #ff9a5f);
--g-primary-contrast: light-dark(#fff, #000);
--g-secondary: light-dark(#066, #1be4a4);
--g-secondary-contrast: light-dark(#fff, #000);
--g-success: light-dark(#176b3d, #47ce7f);
--g-success-contrast: light-dark(#fff, #1a1a1a);
--g-warning: light-dark(#c95100, #fea359);
--g-warning-contrast: light-dark(#fff, #1a1a1a);
--g-danger: light-dark(#b32525, #ff9a9a);
--g-danger-contrast: light-dark(#fff, #1a1a1a);
--g-muted: light-dark(#5a635e, #b0b0b0);
--g-muted-contrast: light-dark(#fff, #1a1a1a);
--g-hover: var(--g-secondary);
--g-hover-contrast: var(--g-secondary-contrast);
--g-active: light-dark(#268055, #ffc499);
--g-active-contrast: light-dark(#fff, #1a1a1a);
--g-bg: light-dark(#fff, #171a18);
--g-bg-contrast: light-dark(#1a201c, #eee);
--g-stripe: light-dark(#0441, #0ee1);
--g-fg: light-dark(#c2ecdc, #30443d);
--g-fg-contrast: light-dark(#000, #eee);
--g-h-color: light-dark(#16211b, #f7fafc);
--g-a-color: light-dark(#0b6478, #8faaf9);
--g-button-bg: var(--g-primary);
--g-button-color: var(--g-primary-contrast);
--g-button-border-color: var(--g-primary);
--g-input-bg: light-dark(#fafffc, #40584d);
--g-input-color: light-dark(#050505, #fff);
--g-input-border-color: light-dark(#7a9a8c, #93b79f);
--g-border-color: light-dark(#a5d3c1, #3f8363);
--g-border-radius: 12px;
--g-border-width: 1px;
--g-arrow-width: .6em;
--g-arrow-height: .36em;
--g-arrow-image-light: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2010%206%22%20fill%3D%22none%22%20stroke%3D%22%23050505%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M1%201%205%205%209%201%22%2F%3E%3C%2Fsvg%3E');
--g-arrow-image-dark: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2010%206%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M1%201%205%205%209%201%22%2F%3E%3C%2Fsvg%3E');
--g-arrow-image: var(--g-arrow-image-light);
--g-padding-t: 8px;
--g-padding-r: 8px;
--g-padding-b: 8px;
--g-padding-l: 8px;
--g-padding-ratio2: 1.5;
--g-padding-ratio3: 2.5;
--g-padding: var(--g-padding-t) var(--g-padding-r) var(--g-padding-b) var(--g-padding-l);
--g-padding-input: var(--g-padding-t) calc(var(--g-padding-r) * var(--g-padding-ratio2)) var(--g-padding-b) calc(var(--g-padding-l) * var(--g-padding-ratio2));
--g-margin-t: 12px;
--g-margin-r: 12px;
--g-margin-b: 12px;
--g-margin-l: 12px;
--g-margin-ratio2: 2.5;
--g-margin-ratio3: 4.25;
--g-grid-min-width: 250px;
--g-portrait-width: 800px;
--g-gap1: 1px;
--g-gap2: 8px;
--g-gap3: 12px;
--g-shadow-tint: color-mix(in oklab, var(--g-secondary) 45%, transparent);
--g-shadow-ambient: light-dark(#171a1826, #0009);
--g-shadow-highlight: light-dark(#fff6, #fff4);
--g-shadow:
0 1px 2px var(--g-shadow-ambient),
0 12px 28px -14px var(--g-shadow-ambient),
0 0 14px -4px var(--g-shadow-tint),
inset 0 0 0 var(--g-border-width) color-mix(in oklab, var(--g-shadow-highlight) 40%, transparent),
inset 0 1px 1px var(--g-shadow-highlight),
inset 0 -10px 18px -14px var(--g-shadow-tint);
--g-drawer-width: 300px;
--g-transition-duration: .3s;
--g-outline: 2px solid var(--g-active);
--g-nav-z: 100;
--g-dropdown-gap: .5em;
--g-dropdown-z: 1000;
--g-modal-z: 10000;
}
:root[data-theme="light"] {
color-scheme: light;
}
@media (prefers-reduced-motion: reduce) {
:root {
--g-transition-duration: 1ms;
}
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--g-arrow-image: var(--g-arrow-image-dark);
}
}
:root[data-theme="dark"] {
color-scheme: dark;
--g-arrow-image: var(--g-arrow-image-dark);
}
Colors are declared once with light-dark() and follow the OS preference out of the box. To let users override it, set data-theme on the root element:
document.documentElement.dataset.theme = 'dark' // or 'light'
delete document.documentElement.dataset.theme // back to the OS preference
Overriding a color for a single theme only takes one declaration:
:root {
--g-bg: light-dark(#fff, #111);
}
Try customizing Goku CSS live! Changes will apply immediately to this page.
Goku CSS provides beautiful defaults for all HTML elements out of the box. No classes needed - just write semantic HTML and get great-looking results.
Buttons are styled with hover states, focus indicators, and disabled states for better accessibility.
Form inputs are consistently styled with proper focus states and responsive design.
A form stacks its fields and spaces them with a gap, so no
markup or classes are needed to lay one out. Put fields on the same row by wrapping them in a
flex container, and reach for
flex-column with a
g* gap when the fields aren't inside a form.
<form>
<div class="flex g2">
<label class="flex-auto">
Text
<input type="text">
</label>
<label class="flex-auto">
Number
<input type="number">
</label>
</div>
<label>
Textarea
<textarea rows="4"></textarea>
</label>
</form>
Radio buttons keep their native appearance by default. Wrap them in a
group with a label after each input to turn them into a
segmented control: the input is hidden visually (still focusable and announced) and the label becomes the
control. See Accessibility for how that pattern works.
<label>
<input type="radio" name="plain-radio" checked> Native radio
</label>
<div class="group flex">
<input type="radio" name="radio" id="radio1">
<label for="radio1" class="flex-1 text-center">
Radio 1
</label>
<input type="radio" name="radio" id="radio2">
<label for="radio2" class="flex-1 text-center">
Radio 2
</label>
<input type="radio" name="radio" id="radio3">
<label for="radio3" class="flex-1 text-center">
Radio 3
</label>
</div>
<label class="label__file">
Dropbox file input
<input type="file">
</label>
<ul class="breadcrumb">
<li>Home</li>
<li><a href="#">link</a></li>
</ul>
Use the vertical class to have a vertical nav.
<nav class="p2 fg">
<div>Nav</div>
<input type="text" placeholder="search">
<input id="nav-toggle2" type="checkbox">
<div class="content">
<a href="#">home</a>
<a href="#">about</a>
<div class="dropdown static right" tabindex="0">
<span>dropdown</span>
<div class="p2">
<div>Item 1</div>
<div>Item 2</div>
</div>
</div>
<button name="button">button</button>
</div>
<label class="toggle" for="nav-toggle2">
<div></div>
<div></div>
<div></div>
</label>
</nav>
| Username | Origin |
|---|---|
| Goku | Vegeta |
| Vegeta | Vegeta |
Pre-built components that work seamlessly together. All components are built with accessibility in mind and work without JavaScript.
Collapsible content sections. Use radio inputs for exclusive groups or checkboxes for independent toggles.
<div class="accordion">
<label for="accordion" class="p1">Click accordion</label>
<input id="accordion" type="checkbox">
<div class="p1 bg" style="height: 1.5em;">
Accordion content
</div>
</div>
A three-column shell with a collapsible panel on each side. The toggles are plain checkboxes, so it works without JavaScript.
<div class="drawer-layout fg-border">
<input id="left-toggle" type="checkbox">
<div>
<div class="fg p1 pb3 h no-border-radius">This is the left drawer.</div>
<label class="absolute bottom left" for="left-toggle"></label>
</div>
<div class="flex-4 pb3">This is the main content.</div>
<input id="right-toggle" type="checkbox">
<div>
<div class="fg p1 pb3 h no-border-radius">This is the right drawer.</div>
<label class="mirror absolute bottom right" for="right-toggle"></label>
</div>
</div>
Versatile dropdown menus with click and hover triggers. Automatically adapts to mobile with slide-up behavior.
<div class="dropdown">
<input id="dropdown1" type="checkbox">
<label for="dropdown1">Click Dropdown</label>
<div class="p3">
<div>Item 1</div>
<div>Item 2</div>
<label for="dropdown1" class="m0 p1">Collapse</label>
</div>
</div>
If you use a button and click on it the dropdown will stay open while the button is focused else use a span or anchor to only open on hover. Add "right" next to the dropdown class to open from the right.
<div class="dropdown" tabindex="0">
<button>Hover Dropdown, you can use button, span or a.</button>
<div class="p2">
<div>Item 1</div>
<div>Item 2</div>
</div>
</div>
Add the class icon to remove the arrow.
<div class="dropdown icon" tabindex="0">
<span>
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" height="32">
<circle cx="8" cy="8" r="7" stroke="#888" fill="none"/>
<circle cx="8" cy="5.5" r="3" fill="#888"/>
<ellipse cx="8" cy="12" rx="6" ry="3" fill="#888"/>
</svg>
</span>
<div class="p2">content</div>
</div>
Add the class -mobile or -desktop next to dropdown to have a mobile/desktop only dropdown
A container that clips its content (group,
border-radius, overflow-x)
cuts the panel off. Add static to let it break out:
the panel then positions against the nearest positioned ancestor instead of the dropdown.
Keep that ancestor unpositioned and unclipped.
right still aligns to the dropdown,
in browsers that support anchor positioning.
<div class="border-radius"> <!-- overflow: hidden -->
<div class="dropdown static right" tabindex="0">
<button>Escapes right</button>
<div class="p2">
<div>Item 1</div>
<div>Item 2</div>
</div>
</div>
</div>
A full-screen overlay that closes on a click outside its last child.
<div class="modal">
<div>last child</div>
</div>
<div class="tabs g3">
<a>link 1</a>
<a class="active">link 2</a>
<a>link 3</a>
<a>link 4</a>
</div>
<div class="py1">content</div>
Powerful utility classes for rapid prototyping and fine-tuning. Mix and match to create any design without writing custom CSS.
The interactive components are built on checkboxes and radios rather than JavaScript, so keyboard and screen reader support comes from the native controls. Two rules make that work:
position: absolute and a 1px box instead of
display: none, so it stays focusable and announced.
--g-outline on
:focus-visible, and for hidden inputs the outline is drawn
on the label the user actually sees.
Tab through the segmented radios in the Input section to see it — the visible label takes the outline.
.group > input[type="radio"] {
/* Hidden visually, still focusable and announced. */
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
&:focus-visible + label {
outline: var(--g-outline);
}
}
Built-in loading animations for better user feedback during async operations.
<button class="spin">button</button>
<button class="ripple">button</button>
You can add/remove the border-radius on an element with the utility class no-border-radius or border-radius.
The class group will add the radius and remove the radius of the children.
The class b adds a border and a radius on every side — it is the frame used around each demo on this page.
The classes bt, br, bb, bl will add a border to the element, "b" is for border then add the side "t" for top, "r" for right, "b" for bottom, and "l" for left.
<div class="b p2">framed</div>
<span class="bt primary-border">top border only</span>
Semantic color system with automatic dark mode support. Each color comes with background, border, and text variants.
Hover the two boxes below to see hover swap the background and hover-text swap the text colour.
Powerful flexbox utilities for modern layouts. Responsive by default with mobile-first breakpoints.
Use the flex-auto class inside an element containing the flex class to automatically fill the space and adapt the orientation to the window size.
Use the classes g1, g2, g3 to add a gap based on the margins values.
You can also use flex-* where * is a digit from 1 to 8.
Use flex-column on the parent if you want to take all the width and stack your elements.
Use flex-row to keep row direction on small screen.
Use flex-wrap to show overflowing elements on a new line.
Use flex-center to align everything in the center.
Modern CSS Grid utilities for 2D layouts. Perfect complement to flexbox for complex grid systems.
Basic grid container with auto-fit columns (uses --g-grid-min-width variable):
<div class="grid-fit g2">
<div class="fg p1"></div>
<div class="fg p1"></div>
</div>
Auto-fill vs auto-fit (auto-fill creates empty columns if space available):
<div class="grid-fill g2">
<div class="fg p1"></div>
<div class="fg p1"></div>
</div>
Column and row spanning for complex layouts:
<div class="grid-fit g2" style="grid-template-columns: repeat(4, 1fr);">
<div class="fg p1 col-2">col-2</div>
<div class="fg p1"></div>
<div class="fg p1"></div>
<div class="fg p1 col-3">col-3</div>
<div class="fg p1"></div>
<div class="fg p1 w">w</div>
</div>
Row spanning example:
<div class="grid-fit g2" style="grid-template-columns: repeat(2, 1fr);">
<div class="fg p1 row-2">row-2</div>
<div class="fg p1"></div>
<div class="fg p1 row-3">row-3</div>
<div class="fg p1"></div>
<div class="fg p1"></div>
<div class="fg p1"></div>
</div>
Available classes:
grid - Basic grid containergrid-fit - Auto-fit columns (uses --g-grid-min-width)grid-fill - Auto-fill columns (uses --g-grid-min-width)col-2, col-3 - Column spanning (2 or 3 columns)w - Span entire widthrow-2, row-3 - Row spanning (2 or 3 rows)g1, g2, g3 - Gap sizing (works with both flex and grid)Note: The grid system uses CSS variables for flexibility. Set --g-grid-min-width to customize the minimum column width for auto-fit and auto-fill grids.
Scroll the content of an element with overflow-x and overflow-y.
Keep text on one line with the line class.
The portrait class centers an element and caps its width at
--g-portrait-width (800px by default), which keeps lines of text
short enough to read comfortably. This page's content is a portrait.
:root {
--g-portrait-width: 960px;
}
Change the position property with the classes
absolute, fixed, relative, static, sticky
and set the side position to 0 with
top, right, bottom, left
or to 100% with
top-100, right-100, bottom-100, left-100.
The w class takes 100% of the width and the h class takes 100% of the height.
You can try to add x, y, t, r, b or l before the size of the margin or padding to affect only some sides (e.g. my1, px1). For the margin, if you don't specify a size, like m or mx, the margin will be set to auto.
You can use the class shadow and no-shadow to add or remove shadow.