CSS Framework / v0.9.9
ply
A ratio-based CSS framework built for AI agents, accessible by default, and small enough to ship anywhere. One stylesheet. ~18KB gzipped. No JavaScript, no build step, no configuration.
Rationale
Why ply exists
CSS frameworks were designed for humans reading documentation. But increasingly, the first consumer of your framework is an AI — a coding agent in Replit, Claude, Cursor, or Copilot generating a UI from a prompt. ply is designed for that world.
AI-Native
Ships with PLY.md and ply-classes.json — a complete class reference any AI agent can read. Class names are predictable: .alert-blue, .btn-sm, .unit-50. An LLM guesses correctly on the first try.
Accessible by Default
Accessibility is a civil rights issue. When your UI isn't accessible, you exclude people. ply bakes :focus-visible outlines, prefers-reduced-motion, semantic HTML styling, and WCAG AA contrast into the CSS itself.
Small Footprint
~18KB gzipped, full framework. No JavaScript runtime. No build step. No tree-shaking. One <link> tag. That matters when you're spinning up prototypes in Replit or shipping to users on slow connections.
Native Elements
Every HTML element, styled
ply styles semantic HTML so you get good defaults without adding classes. Below is every native element ply enhances.
Headings
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Inline Text
This is a paragraph with bold text, italic text, a link, inline code, Ctrl+S, highlighted text, small text, abbr, and deleted text.
Superscript: x2 and subscript: H2O.
123 Example St, Internet, WWW 00000Blockquote
Design is not just what it looks like and feels like. Design is how it works.
Ordered List
- First item
- Second item
- Nested item
- Third item
Unordered List
- First item
- Second item
- Nested item
- Third item
Definition List
- ply
- A ratio-based CSS framework built for AI agents.
- Accessible
- Designed to be usable by everyone, including people who rely on assistive technology.
- Ratio-based
- Grid columns defined as percentages of the parent:
unit-50,unit-33,unit-25.
Details & Summary
Click to expand — native HTML disclosure
This is a <details> element. It works with keyboard navigation, screen readers, and requires zero JavaScript. ply styles the indicator and spacing.
Another disclosure widget
Multiple details elements stack naturally.
Preformatted Code
<div class="units-row">
<div class="unit-50 tablet-unit-100">
<h2>Left column</h2>
<p>Content here.</p>
</div>
<div class="unit-50 tablet-unit-100">
<h2>Right column</h2>
<p>More content.</p>
</div>
</div>
Table
| Feature | ply | Tailwind | Bootstrap |
|---|---|---|---|
| Size (gzip) | ~18KB | ~10KB+ (varies) | ~30KB |
| Build step | No | Yes | No |
| AI instruction file | PLY.md + JSON | No | No |
| Dark mode | Automatic | Automatic (v4) | Attribute-based |
| Custom themes | CSS custom properties | Config / CSS | Sass + CSS vars |
| Focus styles | Built-in | Utility-based | Built-in |
| Reduced motion | Built-in | Utility-based | Built-in |
Form Elements
Checkboxes & Radios
Input States
Input Groups
Progress & Meter
Dialog (Modal)
Native <dialog> element — no JavaScript library required.
Horizontal Rule
Content above the rule.
Content below the rule.
Components
ply components
Buttons
Button Group
Alerts
Alert Variants
Navigation Pills
Tabs
Breadcrumbs
Pagination
Labels
Default Blue Red Green Yellow BlackElevation
Layers & Depth
ply provides a 4-level elevation system using CSS custom properties. Each layer has a background and corresponding shadow that adapts across all themes.
Base surface. No elevation.
Cards, panels. Subtle lift.
Dropdowns, popovers.
Modals, dialogs.
Shadow Utilities
Apply shadows independently with .shadow-1, .shadow-2, .shadow-3:
.shadow-1 — subtle
.shadow-2 — medium
.shadow-3 — pronounced
CSS Custom Properties
/* Use in your own CSS */
.my-card {
background: var(--ply-layer-1);
box-shadow: var(--ply-shadow-2);
}
/* Or use the utility classes */
<div class="layer-2">Elevated content</div>
<div class="shadow-1">Subtle shadow only</div>
Theming
Build Your Own Theme
ply uses CSS custom properties (--ply-*) for every color, border, shadow, and layer. To create a custom theme, override these tokens in a [data-theme] selector and set that attribute on <html>. No Sass, no build step.
Three steps
<!-- 1. Define your theme in a <style> block or stylesheet -->
<style>
[data-theme="brand"] {
--ply-bg-body: #fefce8;
--ply-bg-surface: #ffffff;
--ply-color-body: #1a1a1a;
--ply-color-link: #b45309;
--ply-border-color: #d6d3d1;
--ply-btn-default-bg: #b45309;
--ply-btn-default-hover: #92400e;
/* override as many or as few tokens as you need */
}
</style>
<!-- 2. Activate it -->
<html data-theme="brand">
<!-- 3. Or switch at runtime -->
<script>
document.documentElement.setAttribute('data-theme', 'brand');
</script>
Built-in modes
ply ships with light and dark. Dark mode activates automatically via prefers-color-scheme, or you can force it with data-theme="dark".
Everything adapts
All components — alerts, buttons, forms, navigation, tables, layers — read from --ply-* tokens. Override the tokens and every component follows.
Token reference
| Token | Purpose |
|---|---|
--ply-bg-body | Page background |
--ply-bg-surface | Card / panel background |
--ply-bg-surface-alt | Alternating surface |
--ply-bg-muted | Muted / disabled background |
--ply-color-body | Primary text |
--ply-color-headings | Heading text |
--ply-color-secondary | Secondary text |
--ply-color-muted | Muted text |
--ply-color-text-inverse | Text on dark buttons |
--ply-border-color | Default borders |
--ply-color-link | Link color |
--ply-color-focus | Focus ring |
--ply-color-input-bg | Input background |
--ply-color-input-border | Input border |
--ply-btn-default-bg | Default button |
--ply-nav-bg | Navigation background |
--ply-layer-0–3 | Elevation backgrounds |
--ply-shadow-1–3 | Elevation shadows |
See custom-theme.html for a full working example with two custom themes (Midnight and Warm).
Typography
Type Scale
text-5xl
text-4xl
text-3xl
text-2xl
text-xl
text-lg
text-base
text-sm
text-xs
Install
Get Started
Add one line to your HTML:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ply-css@1/dist/css/ply.min.css">
That's it. Write semantic HTML. ply handles the rest.