Card text
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/alsanan/weblin/css.css">
<button primary>
<button secondary>
<button ghost>
<button destructive>
<button success>
<button call-to-action>
<button round> <button fab>
size="s|m|l" <button fullwidth>
<article><header>…</header>…</article>
Card text
<article inverted> (testimonials)
<article borderless> (shadow only)
<article flat> (border only)
Etiam venenatis nisl ut orci consequat, vitae tempus quam commodo.
<article selectable>
<input type="checkbox">…
<kbd success|warning|danger|info>
<kbd solid> (black variant)
successwarningdangerinfosolid
<div stack> (column)
<div rows> (row, wrap)
style="--gap:2rem"
<hr vertical>
A
scroll-x / scroll-y
<div cluster>
<div repel>
<div switcher>
style="--switcher-threshold:40rem"
<div split>
style="--split-thresold:10rem"
Fixed 10rem width. Full width when stacked.
Takes remaining space. Stacks below 30rem.
<div field>
<label>…<input>
<small>hint</small>
</div>
<fieldset role="group">
size="xxs|xs|s|m|l|xl|auto"
<label label-float text="…">
<input placeholder=" ">
</label>
<input otp inputmode="numeric" autocomplete="one-time-code" maxlength="6" pattern="\d{6}" required>
<fieldset segmented>
<label><input type=radio>…
<input type="checkbox" switch>
<select><option>…
size="s" (compact variant)
<div slider-wrap>
<input type="range" min="0" max="100">
<output></output>
<table sticky sticky-col>
| Name | Role | Role | Role | Role |
|---|---|---|---|---|
| Ainia | Engineer | Designer | Architect | Developer |
<dl horizontal><dt>type</dt><dd>
<details><summary>…
<img avatar> (adjacent ones overlap)
<dialog> · closedby="any"
<dialog drawer rounded full>
<dialog sheet-top|bottom|left|right>
<button popovertarget="id">
<dialog toast>
<dialog sonner> + <li>
<button commandfor="dlg" command="show-modal">
<dialog id="dlg" closedby="any">
<nav breadcrumb="/">
<menu> / <menu vertical><li>
<aside><nav>… [active] on link
<div tabs> <div tabbar> <input type=radio data-label="…"> </div> <section>panel</section> </div>
aria-busy="true|spinner"
[skeleton] hides children
[shimmer] keeps content
[pulse] box-shadow ring, 1.5s loop
[shine] text shimmer sweep, 2s loop
<progress> (indeterminate — no value)
[ticker] · style="animation-duration:30s"
[lazy-render] + [reveal]
<div carousel>
[tooltip-top|bottom|left|right]
popover="hint" + interestfor
<mark highlight>text</mark>
style="--color: green|red|blue"
default yellow
green
red
blue
@view-transition { navigation: auto; }
[vt="hero"] { view-transition-name: hero; }
presets: hero, title, logo, image, modal, sidebar
<div vt-class="fade">…</div>
classes: fade, scale, slide-up, slide-left, slide-right
vt.js)list→detail morphs, no upfront names
<a href="/product/42" vt-jit>
<script src="js/vt.js"></script>
:transition
<div :transition>…</div>
<div :transition.fade>…</div>
<div :transition.shared="name">…</div>
[vt-none]
<div feature>
<style>
@scope {
& { background: var(--bg-card); }
img { border-radius: 8px; }
}
</style>
</div>
@scope to ([portal]) {
& { border: var(--border); }
}
portals, dialogs stay unstyled
<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
<script src="js/obs.js"></script> <link rel="stylesheet" href="obs.css">adds classes
has-delivery-mode-lite,
has-device-capability-weak to
<html>
1. all state at window._
2. granular reactivity
3. define-components (inline / SFC)