A very lightweight CSS framework. No JavaScript. No complexity. Just clean, composable CSS utilities.
This is a paragraph. Xylem CSS provides sensible typographic defaults so your content looks good out of the box. It uses system-ui fonts for fast rendering and familiar feel.
Here is a link example within body text.
<h1>Heading 1</h1>
<h2>Heading 2</h2>
<h3>Heading 3</h3>
<p>Body text with a <a href="#">link</a>.</p>
<p>Inline <code>code</code> example.</p>
<ul>
<li>Item one</li>
<li>Item two
<ul>
<li>Nested</li>
</ul>
</li>
</ul>
<ol>
<li>First</li>
<li>Second</li>
</ol>
<!-- Unstyled -->
<ul class="list-unstyled">
<li>No bullets</li>
<li>No indentation</li>
</ul>
<ul class="list-unstyled list-divided">
<li>Item with separator</li>
<li>Another item</li>
</ul>
<!-- Works on any parent/children -->
<div class="list-divided">
<div>First</div>
<div>Second</div>
</div>
Inline code: .container, .row, .stack
function hello() {
console.log("Hello");
}
<p>Inline: <code>.container</code></p>
<pre><code>function hello() {
console.log("Hello");
}
</code></pre>
| Name | Role | Status |
|---|---|---|
| Alice | Designer | Active |
| Bob | Developer | Active |
| Carol | Manager | Away |
<table class="w-full">
<thead>
<tr>
<th>Name</th>
<th>Role</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice</td>
<td>Designer</td>
</tr>
</tbody>
</table>
| Name | Role | Status |
|---|---|---|
| Alice | Designer | Active |
| Bob | Developer | Active |
| Carol | Manager | Away |
| Dave | Intern | Active |
<table class="table-striped">
...
</table>
| Name | Role | Status |
|---|---|---|
| Alice | Designer | Active |
| Bob | Developer | Active |
| Carol | Manager | Away |
| Dave | Intern | Active |
<table class="table-hover">
...
</table>
<!-- Combine both -->
<table class="table-striped table-hover">
...
</table>
Content above the rule.
Content below the rule.
<hr>
<span class="badge">Default</span>
<span class="badge badge-primary">Primary</span>
<span class="badge badge-success">Success</span>
<span class="badge badge-danger">Danger</span>
<span class="badge badge-warning">Warning</span>
<span class="badge badge-info">Info</span>
| Task | Status |
|---|---|
| Deploy v2.1 | Done |
| Fix login bug | Urgent |
| Update docs | Pending |
| Research SSR | Planning |
<td><span class="badge badge-success">Done</span></td>
<td><span class="badge badge-danger">Urgent</span></td>
<td><span class="badge badge-warning">Pending</span></td>
<form>
<div class="row">
<div class="stack-sm flex-1">
<label>Name</label>
<input type="text" class="w-full">
</div>
<div class="stack-sm flex-1">
<label>Email</label>
<input type="email" class="w-full">
</div>
</div>
<div class="row">
<div class="stack-sm flex-1">
<label>Password</label>
<input type="password" class="w-full">
</div>
<div class="stack-sm flex-1">
<label>Search</label>
<input type="search" class="w-full">
</div>
</div>
<div class="row">
<div class="stack-sm flex-1">
<label>Phone</label>
<input type="tel" class="w-full">
</div>
<div class="stack-sm flex-1">
<label>URL</label>
<input type="url" class="w-full">
</div>
</div>
<div class="row">
<div class="stack-sm flex-1">
<label>Role</label>
<select class="w-full">
<option>Designer</option>
<option>Developer</option>
<option>Manager</option>
</select>
</div>
<div class="stack-sm flex-1">
<label>Disabled select</label>
<select disabled class="w-full">
<option>Disabled select</option>
</select>
</div>
</div>
<div class="stack-sm">
<label>Plan</label>
<div class="row">
<label><input type="radio" name="plan"> Free</label>
<label><input type="radio" name="plan"> Pro</label>
<label><input type="radio" checked disabled> Disabled radio</label>
</div>
</div>
<div class="stack-sm">
<label>Message</label>
<textarea class="w-full"></textarea>
</div>
<div class="stack-sm">
<label>Terms</label>
<div class="row">
<label><input type="checkbox"> I agree to the terms</label>
<label><input type="checkbox" checked disabled> Disabled checkbox</label>
</div>
</div>
<div class="row mt-4">
<button type="submit" class="button button-primary">Submit</button>
<button class="button" disabled>Disabled</button>
<button class="button button-primary" disabled>Disabled Primary</button>
</div>
</form>
<label>
<span class="switch">
<input type="checkbox" role="switch">
<span class="switch-track"></span>
</span> Notifications
</label>
<!-- Checked -->
<label>
<span class="switch">
<input type="checkbox" role="switch" checked>
<span class="switch-track"></span>
</span> Dark mode
</label>
<!-- Disabled -->
<label>
<span class="switch">
<input type="checkbox" role="switch" disabled>
<span class="switch-track"></span>
</span> Disabled
</label>
<!-- Disabled on -->
<label>
<span class="switch">
<input type="checkbox" role="switch" checked disabled>
<span class="switch-track"></span>
</span> Disabled on
</label>
<div class="row">
<div class="stack-sm flex-1">
<label>Date</label>
<input type="date" class="w-full">
</div>
<div class="stack-sm flex-1">
<label>Date & Time</label>
<input type="datetime-local" class="w-full">
</div>
</div>
<div class="row">
<div class="stack-sm flex-1">
<label>Time</label>
<input type="time" class="w-full">
</div>
<div class="stack-sm flex-1">
<label>Month</label>
<input type="month" class="w-full">
</div>
<div class="stack-sm flex-1">
<label>Week</label>
<input type="week" class="w-full">
</div>
</div>
<div class="row">
<div class="stack-sm flex-1">
<label>Quantity</label>
<input type="number" placeholder="1" min="0" max="100" class="w-full">
</div>
<div class="stack-sm flex-1">
<label>Price</label>
<input type="number" placeholder="9.99" step="0.01" min="0" class="w-full">
</div>
</div>
<div class="row">
<div class="stack-sm flex-1">
<label>Range</label>
<input type="range" min="0" max="100" value="50" class="w-full">
</div>
<div class="stack-sm flex-1">
<label>Color</label>
<input type="color" value="#4a9f4a" class="w-full">
</div>
</div>
<div class="row">
<div class="stack-sm flex-1">
<label>File</label>
<input type="file" class="w-full">
</div>
<div class="stack-sm flex-1">
<label>Native button</label>
<button type="button" class="w-full">Native Button</button>
</div>
</div>
A text-only card without an image. Clean and minimal.
<!-- Card with image -->
<div class="card">
<img class="card-img" src="image.jpg" alt="...">
<div class="card-body">
<h3 class="card-title">Title</h3>
<p class="card-text">Description text.</p>
<a class="button button-primary" href="#">Action</a>
</div>
</div>
<!-- Card with footer -->
<div class="card">
<div class="card-body">
<h3 class="card-title">Title</h3>
<p class="card-text">Description text.</p>
</div>
<div class="card-footer">Footer content</div>
</div>
<!-- Shadow card -->
<div class="card card-shadow">
<div class="card-body">
<h3 class="card-title">Title</h3>
<p class="card-text">Elevated card.</p>
</div>
</div>
Use .hero for standard size or add .hero-lg for more vertical space.
A .bg-subtle class helps the hero stand out from the rest of the page.
<section class="hero">
<h1>Small Hero</h1>
<p>Use .hero for standard size or add .hero-lg for more vertical space.</p>
<button class="button button-primary">Get Started</button>
</section>
<!-- Large variant -->
<section class="hero hero-lg">
<h1>Big Hero</h1>
<p>More vertical space.</p>
</section>
<!-- With subtle background -->
<section class="hero bg-subtle">
<h1>Build Something Great</h1>
<p>A .bg-subtle class helps the hero stand out from the rest of the page.</p>
</section>
CSS-only dropdowns using :focus-within. No JavaScript required. Wrap a .dropdown-toggle button and a .dropdown-menu list inside a .dropdown container.
<div class="dropdown">
<button class="dropdown-toggle">Options</button>
<ul class="dropdown-menu">
<li><a href="#">Edit</a></li>
<li><a href="#">Duplicate</a></li>
<li><a href="#">Archive</a></li>
</ul>
</div>
Use <hr class="dropdown-divider"> to separate groups of actions.
<div class="dropdown">
<button class="dropdown-toggle">Actions</button>
<ul class="dropdown-menu">
<li><a href="#">New file</a></li>
<li><a href="#">New folder</a></li>
<li><hr class="dropdown-divider"></li>
<li><a href="#">Import</a></li>
<li><a href="#">Export</a></li>
<li><hr class="dropdown-divider"></li>
<li><a href="#">Delete</a></li>
</ul>
</div>
Note: Dropdowns use :focus-within and close when focus leaves the container. Inside a navbar, the last dropdown automatically right-aligns to stay within the viewport. For keyboard Escape support and aria-expanded state, add JavaScript.
<!-- Default (primary) -->
<div class="progress">
<div class="progress-bar" role="progressbar"
aria-valuenow="75" aria-valuemin="0" aria-valuemax="100"
style="width: 75%"></div>
</div>
<!-- Color variants -->
<div class="progress progress-success">
<div class="progress-bar" role="progressbar"
aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"
style="width: 100%"></div>
</div>
<div class="progress progress-warning">
<div class="progress-bar" role="progressbar"
aria-valuenow="45" aria-valuemin="0" aria-valuemax="100"
style="width: 45%"></div>
</div>
<div class="progress progress-danger">
<div class="progress-bar" role="progressbar"
aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"
style="width: 20%"></div>
</div>
<div class="progress progress-info">
<div class="progress-bar" role="progressbar"
aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"
style="width: 60%"></div>
</div>
CSS-only toggle switch. Wrap a checkbox inside .switch with a .switch-track sibling.
<label>
<span class="switch">
<input type="checkbox" role="switch">
<span class="switch-track"></span>
</span> Label text
</label>
<!-- Checked -->
<span class="switch">
<input type="checkbox" role="switch" checked>
<span class="switch-track"></span>
</span>
<!-- Disabled -->
<span class="switch">
<input type="checkbox" role="switch" disabled>
<span class="switch-track"></span>
</span>
Circular avatar images with size variants and an interactive toggle for dropdowns.
Use .avatar-toggle as a button wrapper — adds a caret and focus ring. Pairs with .dropdown.
<!-- Sizes -->
<img class="avatar-sm avatar" src="https://i.pravatar.cc/64?img=11" alt="User">
<img class="avatar" src="https://i.pravatar.cc/64?img=11" alt="User">
<img class="avatar-lg avatar" src="https://i.pravatar.cc/64?img=11" alt="User">
<!-- Avatar toggle (pairs with .dropdown) -->
<div class="dropdown">
<button class="avatar-toggle">
<img class="avatar" src="https://i.pravatar.cc/64?img=11" alt="User">
</button>
<ul class="dropdown-menu">
<li><a href="#">Profile</a></li>
<li><a href="#">Sign out</a></li>
</ul>
</div>
A horizontal rule with optional inline text, commonly used for "or" separators in forms.
<div class="divider">or</div>
<div class="divider">continue with</div>
Page navigation with active state. Wrap in a <nav> with aria-label for accessibility.
<nav aria-label="Pagination">
<ul class="pagination">
<li><a href="#">«</a></li>
<li><span class="active">1</span></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">»</a></li>
</ul>
</nav>
Centered wrapper, max-width 960px.
<div class="container">
<!-- Centered, max-width 960px -->
</div>
<div class="row">
<div>One</div>
<div>Two</div>
<div>Three</div>
</div>
<!-- Modifiers -->
<div class="row row-center">...</div>
<div class="row row-between">...</div>
<div class="row row-nowrap">...</div>
.row-between<div class="row row-between">
<div>Left</div>
<div>Right</div>
</div>
Responsive grid that auto-fills columns. Override --xy-grid-min to change minimum column width (default 16rem).
<div class="grid">...</div>
<!-- Custom column width -->
<div class="grid" style="--xy-grid-min: 10rem;">...</div>
<div class="stack">...</div>
<div class="stack-sm">...</div>
<div class="stack-lg">...</div>
.p-1.p-2.p-3.p-4.p-5<!-- Margin -->
<div class="m-0">...</div> <!-- 0 -->
<div class="m-1">...</div> <!-- 0.25rem -->
<div class="m-2">...</div> <!-- 0.5rem -->
<div class="m-3">...</div> <!-- 1rem -->
<div class="m-4">...</div> <!-- 1.5rem -->
<div class="m-5">...</div> <!-- 2rem -->
<!-- Padding -->
<div class="p-0 .. p-5">...</div>
<!-- Directional (mt, mb, ml, mr, pt, pb, pl, pr) -->
<div class="mt-3">...</div> <!-- margin-top -->
<div class="mb-2">...</div> <!-- margin-bottom -->
<div class="ml-3">...</div> <!-- margin-left -->
<div class="mr-3">...</div> <!-- margin-right -->
<div class="pt-3">...</div> <!-- padding-top -->
<div class="pb-2">...</div> <!-- padding-bottom -->
<div class="pl-3">...</div> <!-- padding-left -->
<div class="pr-3">...</div> <!-- padding-right -->
<div class="mx-auto">...</div> <!-- center -->
.d-none — display: none.d-block — display: block.d-flex — display: flex.d-inline — display: inline.d-inline-block — display: inline-block<div class="d-none">Hidden</div>
<div class="d-block">Block</div>
<div class="d-flex">Flex container</div>
<span class="d-inline-block">Inline block</span>
.flex-1.flex-auto.flex-none.items-start, .items-center, .items-end, .items-stretch.justify-start, .justify-center, .justify-end, .justify-betweenAbove: .gap-1 (0.25rem). Available: .gap-0 through .gap-5.
<!-- Flex grow -->
<div class="d-flex gap-3">
<div class="flex-1">Equal share</div>
<div class="flex-auto">Natural size, grows</div>
<div class="flex-none">Fixed</div>
</div>
<!-- Alignment -->
<div class="d-flex items-center justify-between">
<div>Left</div>
<div>Right</div>
</div>
<!-- Gap -->
<div class="d-flex gap-2">...</div>
All display and spacing utilities support -sm (≥576px), -md (≥768px), and -lg (≥1024px) breakpoint variants.
| Class | Applies at |
|---|---|
.d-sm-none | ≥ 576px |
.d-md-block | ≥ 768px |
.d-lg-flex | ≥ 1024px |
| Class | Applies at |
|---|---|
.p-sm-{0-5}, .m-sm-{0-5} | ≥ 576px |
.p-md-{0-5}, .m-md-{0-5} | ≥ 768px |
.p-lg-{0-5}, .m-lg-{0-5} | ≥ 1024px |
<!-- Hidden on mobile, visible from md breakpoint -->
<div class="d-none d-md-block">Desktop only</div>
<!-- Visible on mobile, hidden from md breakpoint -->
<div class="d-md-none">Mobile only</div>
<!-- Responsive spacing -->
<div class="p-2 p-md-3 p-lg-4">
Padding grows with screen size
</div>
<!-- Responsive margin -->
<div class="m-1 m-sm-2 m-lg-4">
Margin grows with screen size
</div>
.text-xs — 0.7rem
.text-sm — 0.85rem
.text-md — 0.9rem
.text-base (default) — 1rem
.text-lg — 1.15rem
.text-muted
.text-primary
.text-success
.text-danger
.text-warning
.text-info
.font-light (300)
.font-semibold (600)
Uppercase muted label
Large, light-weight number for stats and pricing.
Inline flex row for metadata (dates, authors, badges).
.text-left
.text-center
.text-right
Use .w-full for width: 100% and .max-w-full for max-width: 100%.
<!-- Size -->
<p class="text-xs">Extra small</p>
<p class="text-sm">Small</p>
<p class="text-md">Medium</p>
<p class="text-base">Base (default)</p>
<p class="text-lg">Large</p>
<!-- Color -->
<p class="text-muted">Muted</p>
<p class="text-primary">Primary</p>
<p class="text-success">Success</p>
<p class="text-danger">Danger</p>
<p class="text-warning">Warning</p>
<p class="text-info">Info</p>
<!-- Font weight -->
<p class="font-light">Light (300)</p>
<p class="font-semibold">Semibold (600)</p>
<!-- Section label / overline -->
<p class="section-label">Section heading</p>
<p class="overline">Overline text</p>
<!-- Display number -->
<div class="display-number">4,200+</div>
<!-- Subtle background -->
<div class="bg-subtle p-3">Highlighted section</div>
<!-- Meta row -->
<div class="meta">
<span>Author</span>
<span>·</span>
<span>Date</span>
</div>
<!-- Alignment -->
<p class="text-left">Left</p>
<p class="text-center">Center</p>
<p class="text-right">Right</p>
<!-- Width -->
<input class="w-full">
<img class="max-w-full">
Override any --xy-* custom property in your own CSS to theme the entire framework.
| Token | Value |
|---|---|
--xy-space-1 | 0.25rem (4px) |
--xy-space-2 | 0.5rem (8px) |
--xy-space-3 | 1rem (16px) |
--xy-space-4 | 1.5rem (24px) |
--xy-space-5 | 2rem (32px) |
| Token | Value |
|---|---|
--xy-text-xs | 0.7rem |
--xy-text-sm | 0.85rem |
--xy-text-md | 0.9rem |
--xy-text-base | 1rem |
--xy-text-lg | 1.15rem |
/* Override tokens to theme the framework */
:root {
--xy-color-primary: #6366f1;
--xy-color-primary-hover: #4f46e5;
--xy-radius: 8px;
--xy-container-width: 1200px;
}
/* All components automatically pick up the new values */
Use .sr-only to visually hide content while keeping it accessible to screen readers.
<!-- Hidden label for screen readers -->
<button class="button button-primary">
Menu <span class="sr-only">— open navigation</span>
</button>
<!-- Skip to content link -->
<a href="#main" class="sr-only">Skip to main content</a>
<!-- Icon-only button with accessible label -->
<button class="button">
× <span class="sr-only">Close</span>
</button>