weblin.org

Cheatsheet · Classless, no-build, native-first, attribute-driven CSS ~17KB framework
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/alsanan/weblin/css.css">

Buttons

Variantsonly one primary per page
<button primary>
<button secondary>
<button ghost>
<button destructive>
<button success>
<button call-to-action>
Shape / size
<button round> <button fab>
size="s|m|l"  <button fullwidth>

Cards

Cardwhite surface, 16px radius
<article><header>…</header>…</article>
Variants
<article inverted> (testimonials)
<article borderless> (shadow only)
<article flat> (border only)
Selectable card
<article selectable>
  <input type="checkbox">…

Badge

soft-tint pills
<kbd success|warning|danger|info>
<kbd solid> (black variant)

Layout

Stack / Rows--gap (default 1rem)
<div stack> (column)
<div rows> (row, wrap)
style="--gap:2rem"
Separator
<hr vertical>
Scroll area
scroll-x / scroll-y
Clusterflex-wrap with gap
<div cluster>
Repelpush apart, stacks when narrow
<div repel>
Switcherrow→column at threshold
<div switcher>
style="--switcher-threshold:40rem"
Splitsidebar + main layout
<div split>
style="--split-thresold:10rem"

Forms

Fieldgroups label + input + hint
<div field>
  <label>…<input>
  <small>hint</small>
</div>
Groupjoined inputs edge-to-edge
<fieldset role="group">
Input
size="xxs|xs|s|m|l|xl|auto"
Floating label
<label label-float text="…">
  <input placeholder=" ">
</label>
Input OTP
<input otp inputmode="numeric" autocomplete="one-time-code" maxlength="6" pattern="\d{6}" required>
Segmentedpill-shaped radio toggle
<fieldset segmented>
  <label><input type=radio>…
Switch
<input type="checkbox" switch>
Select
<select><option>…
size="s" (compact variant)
Slider
<div slider-wrap>
  <input type="range" min="0" max="100">
  <output></output>

Data

Table
<table sticky sticky-col>
Description list
<dl horizontal><dt>type</dt><dd>
Accordionnative <details>, no JS
<details><summary>…
Avatar
<img avatar> (adjacent ones overlap)

Overlays

Conceptual mockups below — real overlays only render once opened by interaction.
Dialognative <dialog>, centered, backdrop
<dialog> · closedby="any"
Dialog
Drawerslides up from the bottom
<dialog drawer rounded full>
Drawer
Sheetedge-anchored panel, 4 directions
<dialog sheet-top|bottom|left|right>
top
bottom
left
right
Popovernative API + anchor positioning
<button popovertarget="id">
Btn
Popover
Toast / Sonnerstacked notifications, corner-anchored
<dialog toast>
<dialog sonner> + <li>
Toast
Toast
Native dialog (no JS)commandfor + command, no script
<button commandfor="dlg" command="show-modal">
<dialog id="dlg" closedby="any">
no-JS

Navigation

Breadcrumb
<nav breadcrumb="/">
Menubar
<menu> / <menu vertical><li>
Sidebar
<aside><nav>… [active] on link
Tabsradio + [tabbar], up to 25
<div tabs>
 <div tabbar>
  <input type=radio data-label="…">
 </div>
 <section>panel</section>
</div>

Feedback / state

Loading
aria-busy="true|spinner"
Skeleton
[skeleton] hides children
[shimmer] keeps content
Pulse / Shinestatic here — no sense animating on a printed sheet
[pulse] box-shadow ring, 1.5s loop
[shine] text shimmer sweep, 2s loop
Progress
<progress> (indeterminate — no value)
Ticker
[ticker] · style="animation-duration:30s"

CSS helpers

Lazy render
[lazy-render] + [reveal]
Carouselscroll-snap, native arrows/dots
<div carousel>
Tooltip (text)
[tooltip-top|bottom|left|right]
Tooltip (rich)
popover="hint" + interestfor
No static preview — hover/scroll interaction needed.

Mark / Highlight

Highlighthand-drawn ink effect via SVG filter
<mark highlight>text</mark>
style="--color: green|red|blue"

View Transitions

Cross-document (MPA)automatic same-origin cross-fade
@view-transition { navigation: auto; }
Name elements
[vt="hero"] { view-transition-name: hero; }
presets: hero, title, logo, image, modal, sidebar
Shared animationhundreds of items, one rule
<div vt-class="fade">…</div>
classes: fade, scale, slide-up, slide-left, slide-right
JIT naming (vt.js)list→detail morphs, no upfront names
<a href="/product/42" vt-jit>
<script src="js/vt.js"></script>
Menut :transition
<div :transition>…</div>
<div :transition.fade>…</div>
<div :transition.shared="name">…</div>
Opt-out
[vt-none]

CSS locality (@scope)

Basic scopeinline <style>, zero JS
<div feature>
  <style>
    @scope {
      & { background: var(--bg-card); }
      img { border-radius: 8px; }
    }
  </style>
</div>
Donut scopeexclude sub-trees
@scope to ([portal]) {
  & { border: var(--border); }
}
portals, dialogs stay unstyled
Baseline: Chrome 143+, Firefox 146+, Safari 26.2+

Theme

Dark / Light toggletwo visible states, system implicit
<meta name="color-scheme" content="light dark">
<script>
  var s = document.querySelector('[name=color-scheme]');
  var t = localStorage.getItem('theme');
  if (t) s.content = t;
</script>
<button onclick="
  var m = document.querySelector('[name=color-scheme]');
  var next = m.content === 'dark' ? 'light' : 'dark';
  m.content = next;
  localStorage.setItem('theme', next);
">Switch theme</button>
CSS uses light-dark() for automatic inversion

Print

no-printhidden when printing
print-onlyhidden on screen, shown when printing
page-break-before/afterforces a page break
avoid-breakkeeps element from splitting
@pageA4 portrait, margin:0 by default

Adaptive performance

Obs.js in <head> + obs.css after css.css
<script src="js/obs.js"></script>
<link rel="stylesheet" href="obs.css">
adds classes has-delivery-mode-lite, has-device-capability-weak to <html>

JS Client microframework

//weblin.org/menut
reactive, single file, no build, ~12.6KB
1. all state at window._
2. granular reactivity
3. define-components (inline / SFC)