Show HN: Sumi.css – CSS 框架
Show HN: Sumi.css – CSS Framework

原始链接: https://github.com/vladocar/Sumi.css

Sumi 是一个极简、无类优先的 CSS 框架,开箱即用样式化语义 HTML,仅在特定布局或变体时使用类。它以单个文件发布(gzipped 后 6.7 KB),无 JavaScript 或依赖,并包含跟随系统偏好的浅色/深色模式,也可通过 `data-theme` 强制设置。每个设计令牌都是 CSS 自定义属性,元素样式使用 `:where()`,因此覆盖时无需 `!important`。它提供可访问的焦点环、ARIA 状态和减少动效支持。它为标题、表单、表格、卡片、手风琴、模态框和导航等常见元素设置样式,并提供容器、网格、按钮、排版和组件的工具类。本地或通过 CDN 链接 `sumi.min.css`,然后自定义令牌。支持现代浏览器。

Hacker News 帖子宣布 Sumi.css,一个由 bepero 开发的 CSS 框架,获得 18 点和 6 条评论。评论者欢迎它,尤其在 PicoCSS 弃用之后,分享无类 CSS 的用例,推荐可直接使用的极简 CSS 资源,报告一个小 bug,并鼓励持续更新。
相关文章

原文
sumi-css-tutorial-hero

A minimalist, classless-first CSS framework. Write plain, semantic HTML and it looks finished. Add a class only when you want something specific.

  • One file, no build step. 6.7 KB minified and gzipped, with no JavaScript and no dependencies.
  • Classless by default. Headings, forms, tables, cards, accordions, modals and navigation are styled from the HTML element alone.
  • Light and dark mode. It follows the visitor's system setting automatically, and data-theme lets you force either mode.
  • Easy to theme. Every color, font, size and radius is a CSS custom property.
  • Easy to override. Element styles have zero specificity, so your own CSS always wins without !important.
  • Accessible. Visible focus rings, ARIA-driven states (aria-invalid, aria-busy, aria-current) and support for reduced motion.

Components · Tutorial · Cheat sheet


Download. Copy sumi.min.css into your project and link it:

<link rel="stylesheet" href="sumi.min.css">

CDN. Load it from this repository through jsDelivr.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/vladocar/Sumi.css/sumi.min.css">

Use sumi.css (unminified) while you develop. Its comments explain every section.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>My page</title>
  <link rel="stylesheet" href="sumi.min.css">
</head>
<body>
  <header>
    <nav>
      <ul><li><strong>My site</strong></li></ul>
      <ul>
        <li><a href="/" aria-current="page">Home</a></li>
        <li><a href="/about">About</a></li>
      </ul>
    </nav>
  </header>

  <main>
    <hgroup>
      <h6>Welcome</h6>
      <h1>Hello, ink.</h1>
    </hgroup>
    <p class="lead">This page is styled by Sumi with almost no classes.</p>
    <a href="#start" role="button">Get started</a>
  </main>

  <footer>
    <p>© 2026 My site</p>
  </footer>
</body>
</html>

<header>, <main> and <footer> center themselves when they are direct children of <body>. Keep the viewport meta tag for phones, and set lang so the browser can hyphenate long-form text correctly.

Element Result
h1–h6, p, lists, blockquote, code, kbd, pre, mark Fluid type scale and consistent vertical spacing
<nav> with two <ul> Navigation bar, left and right groups. Vertical inside <aside>
<button>, [type=submit], <a role="button"> Buttons
<input>, <select>, <textarea>, <label>, <fieldset> Full-width fields with focus rings and helper text
type="checkbox", type="radio", type="range", type="file", type="color" Custom controls that match the theme
<table> Clean rules and tabular numbers
<article> Card. A direct <header> and <footer> become shaded bands
<details> / <summary> Accordion with an animated chevron
<dialog> Modal with a blurred backdrop
<progress>, <meter> Determinate and indeterminate bars

Everything Sumi responds to beyond plain HTML:

Class or attribute Goes on Effect
.container .container-fluid .narrow any block Centered page width (72rem), full width, reading width (44rem)
.grid wrapper Auto-fitting columns, each at least --s-grid-min (14rem) wide
.cols-2 .cols-3 .cols-4 .cols-6 .cols-12 .grid Fixed columns from 48rem up, one column below
.span-2 … .span-9, .span-full grid child Spans several columns
.row .spread .stack wrapper Wrapping flex row, row with space between, vertical stack
.display .lead heading, paragraph Hero headline, larger intro paragraph
.prose .serif text wrapper Long-form reading column, serif typeface
.secondary .contrast .outline button Button colors and outline style
.small .large .full button Sizes, full width
role="button" <a> Link styled as a button
role="group" wrapper Joins buttons, or an input and a button
role="switch" checkbox Toggle switch
aria-invalid="true" / "false" field Red or green validation state
aria-busy="true" any element Loading spinner
aria-current="page" nav link Highlights the current page
data-tooltip="…" data-placement="bottom" any element Tooltip, above or below
data-theme="light" / "dark" <html> or any element Forces a color mode
.dropdown .right <details> Dropdown menu, right-aligned
.callout + .success .warning .danger block Message box
.striped <table> Zebra rows
.muted .small .center .sr-only .overflow-auto any element Utilities
<!-- Buttons -->
<button>Save</button>
<button class="secondary outline">Cancel</button>
<button aria-busy="true">Saving</button>

<!-- Field with helper text and validation -->
<label>Email
  <input type="email" name="email" aria-invalid="true" aria-describedby="email-help">
  <small id="email-help">Enter a valid email address.</small>
</label>

<!-- Switch -->
<label><input type="checkbox" role="switch" checked> Email reminders</label>

<!-- Card -->
<article>
  <header><strong>Six-week course</strong></header>
  <p>From first strokes to a finished hanging scroll.</p>
  <footer><a href="#book" role="button" class="full">€180</a></footer>
</article>

<!-- Dropdown -->
<details class="dropdown">
  <summary>Export as…</summary>
  <ul>
    <li><a href="#">PNG</a></li>
    <li><a href="#">SVG</a></li>
  </ul>
</details>

<!-- Modal: open with dialog.showModal(); the form closes it -->
<dialog id="confirm">
  <article>
    <header><h3>Delete this file?</h3></header>
    <p>This can't be undone.</p>
    <footer>
      <form method="dialog" class="row">
        <button class="secondary outline">Keep</button>
        <button class="contrast">Delete</button>
      </form>
    </footer>
  </article>
</dialog>
<!-- As many columns as fit; stacks on phones -->
<div class="grid">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
</div>

<!-- 12-column layout from tablet width up -->
<div class="grid cols-12">
  <div class="span-8">Main</div>
  <div class="span-4">Sidebar</div>
</div>

<!-- Change the minimum column width for one grid -->
<div class="grid" style="--s-grid-min: 10rem">…</div>

Cards inside a .grid stretch to equal heights and keep their footers aligned at the bottom.

Headings follow a fluid scale. The ratio grows from 1.2 on phones to 1.28 on desktop, and the base size eases from 16px to 17px. No media queries are needed on your side. Line height and letter spacing tighten as headings get larger.

  • <hgroup> pairs a heading with an eyebrow (<h6> before it) and a subtitle (<p> after it).
  • .lead enlarges an intro paragraph. .display makes a hero headline.
  • .prose sets a reading column of about 68 characters with hyphenation. An <article class="prose"> stays plain text instead of becoming a card.
  • .serif switches an element, or the whole <body>, to a system serif stack.

Every design decision is a custom property starting with --s-. Override them in a stylesheet that loads after Sumi:

:root {
  --s-primary: #0f766e;
  --s-radius: .25rem;
  --s-font-body: "Inter", system-ui, sans-serif;
  --s-font-heading: "Fraunces", Georgia, serif;
  --s-container: 64rem;
}

Tokens also work on a single element:

<div class="row" style="--s-primary: #b4235a; --s-radius: 999px">
  <button>Book now</button>
  <button class="outline">Gift card</button>
</div>
Token Controls
--s-primary, --s-on-primary Buttons, links, focus rings, checked controls, and text on them
--s-secondary, --s-contrast Secondary and contrast button colors
--s-bg, --s-surface, --s-card Page, shaded areas, cards
--s-text, --s-muted Body text, secondary text
--s-border, --s-border-strong Rules, card edges, field borders
--s-success, --s-warning, --s-danger Validation and callouts
--s-font-body, --s-font-heading, --s-font-mono, --s-font-serif Typefaces
--s-font-size, --s-line-height, --s-heading-weight Base size, leading, heading weight
--s-text-display, --s-text-h1 … --s-text-h4, --s-text-lead Type scale steps
--s-flow, --s-space Space after text blocks, space after forms and components
--s-container, --s-narrow, --s-measure, --s-gutter Page width, reading width, line length, side padding
--s-radius, --s-shadow, --s-shadow-lg Corners and elevation

The full list is at the top of sumi.css.

Sumi follows the visitor's system setting with no extra code. To force a mode, set data-theme on <html>:

<html lang="en" data-theme="dark">

data-theme also works on any element, so one section can be dark inside a light page. A theme switch that remembers the visitor's choice:

const root = document.documentElement;
const saved = localStorage.getItem('theme');
if (saved) root.dataset.theme = saved;

document.querySelector('#theme-toggle').addEventListener('click', () => {
  const dark = matchMedia('(prefers-color-scheme: dark)').matches;
  const current = root.dataset.theme || (dark ? 'dark' : 'light');
  const next = current === 'dark' ? 'light' : 'dark';
  root.dataset.theme = next;
  localStorage.setItem('theme', next);
});

The dark palette appears twice in sumi.css: once for the system setting and once for data-theme="dark". If you change dark colors, change both blocks.

Element styles are wrapped in :where(), which has zero specificity, so a plain selector is enough to override them:

/* Sumi: :where(blockquote) { border-left: 3px solid … } */
blockquote { border-left-color: var(--s-warning); }

Build your own components from Sumi's tokens and they follow your theme and dark mode automatically:

.badge {
  padding: .1em .55em;
  border: 1px solid var(--s-border);
  border-radius: 999px;
  background: var(--s-surface);
  color: var(--s-muted);
  font-size: .8rem;
}

Current versions of Chrome, Edge, Firefox and Safari. Sumi uses :where(), :has() and color-mix(), which are available from Chrome and Edge 111, Firefox 121 and Safari 16.2. Older browsers still show readable pages, with plainer hover and focus colors.

File Purpose
sumi.css Source with comments, for development
sumi.min.css Minified build, for production
docs.html Every component with its HTML, a light/dark switch and a live token editor
tutorial.html Step-by-step guide to building a page with Sumi

To rebuild the minified file after editing the source:

npx lightningcss-cli --minify sumi.css -o sumi.min.css

MIT © 2026 Vladimir Carrer

联系我们 contact @ memedata.com