Enough CSS to get started
/** in your CSS **/
@import 'https://unpkg.com/nokss';
nokss is a drop-in stylesheet that works out of the box. No need to use CSS classes: just write plain, semantic HTML, and nokss will style it for you (as much as it can). nokss is designed for prototyping, blogs, articles, small projects, and as a starting point for more invloved applications.
- Its fully responsive.
- Supports dark mode out of the box.
- Extensive theming and customization via CSS variables.
Except a few specific elements, this document is styled with nokss itself, and acts as a preview. You can also change the theme in Theming section to see how it would look with other themes.
Installation
Import in your CSS:
@import 'https://unpkg.com/nokss';
Or link in your HTML:
<link rel="stylesheet"
href="https://unpkg.com/nokss" />
You can use other CDNs as well:
You can also self-host the stylesheet by downloading it. If you have a bundler that handles CSS imports, install the library from NPM and then load the locally installed version:
npm install nokss
@import 'node_modules/nokss/dist/nokss.css';
Bundles
If you need smaller stylesheets, use one of the following, smaller, partial bundles:
Name | Size | URL | Description |
---|---|---|---|
Markdown |
|
Styles standard markdown elements. | |
Basic |
|
Markdown plus buttons. | |
Slick |
|
Basic plus general layouts. | |
Formal |
|
Basic plus form elements. | |
Slick Formal |
|
Slick plus form elements. |
About Bundles
Toolbars, modals, feeds and cards are not included in any of the partial bundles. They are only available in the full bundle. Also make sure to use one bundle at a time, otherwise you'll end up with duplicate styles.
Usage
Just load the stylesheet. nokss styles elements based on their semantics, so you don't have to worry about class names. The more semantic and accessible your markup is, the more nokss will be able to do for you.
Semantics are mainly dictated by HTML tags and ARIA roles, with HTML and ARIA attributes providing further context. Structural conventions are used for context about more complex semantics:
<a role="button"> ... </a>
☝️ This anchor is styled like a button.
<menu role="toolbar" aria-orientation="vertical">...</menu>
☝️ This menu is styled as a vertical toolbar.
<input type="email" />
<span role="alert">Please enter a valid email</span>
☝️ The <span/>
is treated as the error message for the input.
<button role="switch">
<span>Off</span>
<span>On</span>
</button>
☝️ The first <span/>
is shown in off state, the second in on state.
You can find a comprehensive list of elements and components supported by nokss, how their usage looks like, and their respective customization options, in the following sections:
Theming
nokss exposes CSS variables for customizing the look and feel of your pages. Change the main theme values in the code below and see their effects on the page:
:root {
--primary-text-color: #fff;
--primary-color: #1f6feb;
--roundness: 5px;
}
@media (prefers-color-scheme: light) {
:root {
--background-color: #fffdf9;
--text-color: #393e46;
}
}
@media (prefers-color-scheme: dark) {
:root {
--background-color: #0d1118;
--text-color: #fffcf3;
}
}
Lorem ipsum
dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Variable | Default | Description |
---|---|---|
--primary-color |
#1f6feb |
The primary color for highlighted text and actions. |
--primary-text-color |
#fff |
The color of the text that is to be used on backgrounds with primary color. |
--text-color |
#393e46 (light)#fffcf3 (dark) |
Color of text. |
--background-color |
#fffdf9 (light)#0d1118 (dark) |
Color of background. |
--danger-color |
#ff2626 |
Denotes potentially harmful actions, or error states. |
--danger-text-color |
#fff |
Denotes potentially harmful actions, or error states. |
--roundness |
5px |
Determines how round various elements are (e.g. border radius). |
--border-expression |
0.05 |
Determines how visible borders are, 0 being invisible and 1 being fully visible. |
--spacing |
8px |
Specifies the spacing between various elements. It is highly recommended to modify spacing per element instead of tweaking the global version. |
Besides these global values, each element also relies on its own specific variables, which are by default calculated from these global values, and can be overriden for customizing the look of individual elements.
Typography
nokss defaults to a native font stack for performance, available in --font-family
CSS variable. Font size is based off of rem
unit to support browser's default font size. Typography is controlled via the following
CSS variables and can be tweaked accordingly:
Variable | Default | Description |
---|---|---|
--font-family |
-apple-system, |
Base font family |
--font-size |
1rem |
Base font size |
--small-font-size |
0.833rem |
Font size for smaller items. |
--font-weight |
300 |
Base font weight |
--line-height |
calc(var(--font-size) * 1.5) |
Base line height |
In most cases you don't need to use these variables, as
rem
unit is itself relative to font size of root.
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure.
Interactions
The following CSS variables generally determine various interactive aspects of the design:
Variable | Default | Description |
---|---|---|
--interactable-size |
32px |
Size of interactable elements. |
--disabled-opacity |
0.35 |
Opacity of disabled elements. |
--active-opacity |
0.85 |
Opacity of active elements. |
--interactable-brightness |
0.96 (light)2.25 (dark) |
Brightness of interactable elements. |
--hover-brightness |
calc(1 - (1 - (light)calc( (dark) |
Brightness of interactable elements on hover. |
On touch devices, a larger --interactable-size
is used:
@media (hover: none) and (pointer: coarse) {
:root {
--interactable-size: 40px;
}
}
Controls
In the following sections you can find details on control elements (buttons, links, toolbars, etc.) that nokss supports, alongside example usage and customization guidelines for each.
Buttons
Use <button>
, <a role="button">
or <input type="submit">
to create buttons:
<button>Button</button>
<a role="button">Anchor</a>
<input type="submit" value="Submit" />
Button Groups
Group buttons to give all but the last one a secondary actions style:
<menu role="group">
<button>Secondary</button>
<button>Primary</button>
</menu>
Use align
attribute to align buttons in a menu to the left or right:
<menu role="group" align="right">
<button>Secondary</button>
<button>Primary</button>
</menu>
Since
align
is unfortunately obsolete, for grouping menus you can use CSS withtext-align
instead:
<menu role="group" style="text-align: right"> ... </menu>
Icon Buttons
Use aria-label
to style buttons as icon buttons:
<button aria-label="send">➤</button>
Using Icon Fonts
Here, an icon font is being used for the icon to be displayed. However, using icon fonts MUST BE done with a lot of care, as they mostly cause accessibility issues. Use an icon font that falls back to characters browsers can display naturally even if the font is not loaded (or is changed), without scrambling the layout.
For this example, and this document in general, graphis icon font is used, which uses similar looking emojis as fallback.
Disabled Buttons
Buttons having disabled
attribute will be styled as disabled:
<button disabled>Disabled</button>
Dangerous Buttons
Denote a dangerous action by starting aria-description
of the button with "danger"
(or "Danger"
):
<button aria-description="dangerously removes the file">Remove</button>
<menu role="group">
<button aria-description="Danger: deletes the draft">
Delete Draft
</button>
<button>Publish</button>
</menu>
Status Badges in Buttons
Use role="status"
to add a status badge (such as a counter) to a button:
<button>
Star <span role="status">3</span>
</button>
<menu role="group">
<button>
Watch <span role="status">450k+</span>
</button>
<button>
Fork <span role="status">32</span>
</button>
<button aria-description="danger">
Delete <span role="status">∞</span>
</button>
</menu>
Keyboard Shortcuts
Use <kbd>
elements inside a button to show keyboard shortcuts:
<menu role="group">
<button>
<kbd>Esc</kbd><span>Cancel</span>
</button>
<button>
<span>Accept</span><kbd>↵</kbd>
</button>
</menu>
Make sure you wrap the label of your button in a separate element so it can be properly spaced.
Customization
Tweak global CSS variables for customizing buttons. If you need further customization, you can use following CSS variables:
Variable | Default | Description |
---|---|---|
--button-height |
var(--interactable-size) |
Height of the button |
--button-min-width |
calc(var(--button-height) * 3) |
Minimum width of the button |
--button-padding |
0 var(--spacing) |
Padding of the button |
--primary-button |
1.2 |
Brightness of primary buttons when hovered. |
--primary-button |
0.9 (light)1.2 (dark) |
Brightness of border of primary buttons (relative to their background). |
--secondary-button |
calc(1 - (1 - (light)
var(--active-opacity) (dark) |
Opacity of secondary buttons when active. |
For example, you can create a slimmer rounded button by setting the following variables:
button {
--button-height: calc(var(--interactable-size) * 0.75);
--roundness: var(--button-height);
}
Or you can create a .fab
class for floating action buttons:
.fab {
--button-height: calc(var(--interactable-size) * 1.5);
--button-min-width: var(--button-height);
--roundness: 50%;
}
<button class="fab">🚀</button>
Toolbars
Use role="toolbar"
to group some buttons together and display them like a toolbar:
<menu role="toolbar">
<button aria-label="edit">✏️</button>
<button aria-label="like">❤️</button>
<button>Longer</button>
</menu>
Use aria-orientation="vertical"
to display a vertical toolbar:
<div style="display: flex; gap: var(--spacing)">
<menu role="toolbar" aria-orientation="vertical">
<button aria-label="done">✔</button>
<button aria-label="delete">🗑</button>
<button aria-label="share">↥</button>
</menu>
<p>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur ...
</p>
</div>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur ...
You can combine vertical toolbars with <details>
element to create
drop-in menus:
<details>
<summary role="button">
Options ▾
</summary>
<div>
<menu role="toolbar" aria-orientation="vertical">
<button>Do this</button>
<button>Or do that</button>
</menu>
</div>
</details>
Options ▾
Or even as part of a composite button:
<menu role="toolbar">
<button>Something</button>
<details>
<summary role="button" aria-label="more">
⋮
</summary>
<div>
<menu role="toolbar" aria-orientation="vertical">
<button>Hellow</button>
<button>World</button>
<button>Goodbye Blue Sky</button>
</menu>
</div>
</details>
</menu>
⋮
Usage Note
Make sure to use
role="button"
on the<summary>
element. Also make sure to wrap the toolbar in a<div>
element so that it can be positioned correctly.
Customization
Tweak global CSS variables for customizing toolbars. If you need further customization, you can use following CSS variables:
Variable | Default | Description |
---|---|---|
--toolbar-spacer-gap |
calc(var(--spacing) * 4) |
Gap between toolbar buttons |
--toolbar-spacer-margin |
calc(var(--spacing) / 2) |
Margin around toolbar spacers |
Tab Lists
Use role="tablist"
to create tab lists:
<nav role="tablist">
<button role="tab" aria-selected="true">First</button>
<button role="tab">Second</button>
<button role="tab">Third</button>
</nav>
Use anchors (<a role="tab">
) instead of buttons for more link-like tab lists:
<nav role="tablist">
<a role="tab" aria-selected="true">First</a>
<a role="tab">Second</a>
<a role="tab">Third</a>
</nav>
The behavior for tab lists needs to be added with JavaScript. Use
aria-selected="true"
to mark a selected element:
document.querySelectorAll( '[role="tablist"]>:is(button, [role="tab"]]' ).forEach(button => { button.addEventListener('click', () => { /* Make sure only one item is selected at a time */ button.parentNode.querySelectorAll(':is(button, [role="tab"])') .forEach(btn => btn.setAttribute('aria-selected', false)) button.setAttribute('aria-selected', true) }) })
You can also use role="status"
within tab panels:
<nav role="tablist">
<a role="tab" aria-selected="true">Friends <span role="status">17</span></a>
<a role="tab">Follwers <span role="status">32</span></a>
</nav>
Switches
Use role="switch"
on buttons to turn them into switches that can be turned on or off:
<button role="switch" aria-checked="false">
<span>Inactive</span>
<span>Active</span>
</button>
A switch must have either one or two child elements, the first one acting as the label for inactive state, the second one for the active state.
<menu role="toolbar">
<button role="switch" aria-checked="false" aria-label="camera">
<span>🎦</span>
<span>🎥</span>
</button>
<button role="switch" aria-checked="true" aria-label="microphone">
<span>🎤</span>
<span>🎙</span>
</button>
<button aria-label="raise hand">🤚</button>
<button aria-label="end call" aria-description="dangerously end call">
📵
</button>
</menu>
Switches can also denote dangerous states by adding an aria-description
attribute starting with "danger"
(or "Danger"
):
<button role="switch"
aria-checked="false"
aria-description="danger: turns on expert mode.">
<span>Expert Mode: Off</span>
<span>Expert Mode: On</span>
</button>
The behavior of switches needs to be added with JavaScript. Use
aria-checked="true"
to turn the switch on or off:
document.querySelectorAll('[role="switch"]').forEach(sw => { sw.addEventListener('click', () => { sw.setAttribute( 'aria-checked', sw.getAttribute('aria-checked') === 'true' ? 'false' : 'true' ) }) })
Inputs
In the following sections you can find details on input elements (text, radio, range, etc.) that nokss supports, alongside example usage and customization guidelines for each.
Text Input
Use the input
tag (with proper type) to create styled inputs:
<input type="email" placeholder="your email"/>
<input type="password" placeholder="your password"/>
<menu role="group" align="end">
<button>Forgot Password</button>
<button>Login</button>
</menu>
Use textarea
tag for larger inputs:
<textarea placeholder="Write something ..."></textarea>
<menu role="group" align="end">
<button>Save Draft</button>
<button>Publish</button>
</menu>
You can use inputs inside horizontal toolbars:
<menu role="toolbar">
<input type="search" placeholder="Search something ..." />
<button aria-label="search">🔍</button>
</menu>
Use attributes and ARIA attributes to determine state of the input:
<input type="text" required placeholder="Required">
<input type="text" aria-invalid="true" placeholder="Invalid"/>
<input type="text" disabled placeholder="Disabled"/>
Invalid Inputs
You can always force invalid style for an input by setting the
aria-invalid
attribute totrue
. This is specifically useful for required inputs, which are not automatically styled as invalid when empty (since CSS cannot track whether the input is touched or not).
Customization
Tweak global CSS variables for customizing inputs. If you need further customization, you can use following CSS variables:
Variable | Default | Description |
---|---|---|
--text-area- |
calc(2 * |
Minimum height of text area |
--input- |
var(--background-color) |
Border color of input when idle |
--input- |
0.4 (light)0.15 (dark) |
Expression for calculating placeholder color |
--input- |
calc(1 - (1 - (light)var(--interactable- (dark) |
Brightness of input background |
--input- |
0.65 (light)0.25 (dark) |
Dropoff of input background brightness when focused |
Radio & Checkboxes
Use <input type="radio">
when users have to pick a choice:
<input type="radio" name="sauce" id="mayo"/>
<label for="mayo">Mayo</label>
<br>
<input type="radio" name="sauce" id="ketchup"/>
<label for="ketchup">Ketchup</label>
Use <input type="checkbox">
when users can independently select and deselect some chocies:
<input type="checkbox" id="step1"/>
<label for="step1">Step 1: Do these stuff</label>
<br>
<input type="checkbox" id="step2"/>
<label for="step2">Step 2: Do that stuff</label>
<br>
<input type="checkbox" id="step3" aria-invalid="true"/>
<label for="step3">Step3: Also you MUST accept this.</label>
<br>
<input type="checkbox" id="final" />
<label for="step3">Everything is done.</label>
Use role="switch"
on an <input type="checkbox" />
to get a switch-style checkbox:
<label>
<input type="checkbox" role="switch"/>
<span>Enable Stuff</span>
</label>
Use role="radiogroup"
instead of radio inputs for a different style:
<menu role="radiogroup">
<button role="radio">Alliance</button>
<button role="radio">Horde</button>
<button role="radio">Panda</button>
</menu>
Note that the behavior needs to be added in when using radio groups:
document.querySelectorAll('[role="radiogroup"]').forEach(group => { group.querySelectorAll('button, [role="radio"]').forEach(radio => { radio.addEventListener('click', () => { group.querySelectorAll('button, [role="radio"]').forEach(r => r.setAttribute('aria-checked', false)) radio.setAttribute('aria-checked', true) }) }) })
Range Input
Use <input type="range">
to create ranges and sliders:
<input type="range" aria-label="some range"/>
Range Input Support
Browsers behave differently in how they allow customization of range inputs. This means that the appearance of your range inputs will vary from browser to browser.
You can use range inputs can in toolbars:
<menu role="toolbar">
<button aria-label="play/pause" role="switch" aria-checked="false">
<span>▶</span>
<span>║</span>
</button>
<button aria-label="stop">▢</button>
<input type="range" aria-label="timeline"
style="--range-input-thumb-width: 16px"/>
<button>Subtitle</button>
</menu>
Using range inputs in vertical toolbars requires a little bit of a work around:
<menu role="toolbar" aria-orientation="vertical">
<div class="--vertical-range-container">
<input type="range" aria-label="volume"/>
</div>
<button aria-label="mute" role="switch" aria-checked="false">
<span>🔇</span>
</button>
</menu>
The length of the track, in this case, is specified by --track-length
CSS variable, which must be set on the container:
.--vertical-range-container {
--track-length: 256px;
}
Customization
Tweak global CSS variables for customizing range inputs. If you need further customization, you can use following CSS variables:
Variable | Default | Description |
---|---|---|
--range-input |
4px |
Height of the track |
--range-input |
var(--button-height) |
Width of the thumb |
--range-input |
calc( |
Height of the thumb |
--range-input |
calc( |
Margin of the range input |
--range-input |
0.12 |
How much to invert the empty track color |
For example, you can create a range input with a smaller, round thumb by setting the following variables:
input[type='range'] {
--range-input-thumb-width: 1.25rem;
--range-input-thumb-height: 1.25rem;
--roundness: 1.25rem;
}
Other Input Types
Other input types are styled roughly similar to text inputs, and can similarly be used in toolbars as well:
<menu role="toolbar">
<select>
<option>To Be</option>
<option>Or not to be</option>
</select>
<input type="color" aria-label="background"/>
<input type="color" value="#4223e9" aria-label="foreground"/>
</menu>
<menu role="toolbar">
<input type="date" value="2023-01-08" aria-label="event date"/>
<input type="time" value="16:20" aria-label="event time"/>
</menu>
You can use color inputs can in vertical toolbars as well:
<menu role="toolbar" aria-orientation="vertical">
<input type="color" value="#FF7B54" aria-label="primary color"/>
<input type="color" value="#FFB26B" aria-label="secodnary color"/>
<input type="color" value="#FFD56F" aria-label="tertiary color"/>
<input type="color" value="#939B62" aria-label="accessory color"/>
<button aria-label="pick" class="icon">⌮</button>
</menu>
Use <input type="file">
for uploading files:
<input type="file" aria-label="profile picture"/>
Field Sets & Grouping
Use field sets to group some form controls together:
<fieldset>
<legend>Login</legend>
<input type="email" placeholder="Email address" />
<input type="password" placeholder="Password" />
<menu role="group" align="end">
<button>Cancel</button>
<button>Login</button>
</menu>
</fieldset>
Labels
Use inputs within labels to bind them together:
<label>
Name: <input type="text" />
</label>
<label>
Phone Number: <input type="tel" />
</label>
<label>
Bio:<br> <textarea></textarea>
</label>
<label>
Account privacy:
<menu role="radiogroup">
<button role="radio">Public</button>
<button role="radio">Private</button>
</menu>
</label>
You can also disassociate labels and inputs:
<label>Name:</label>
<input type="text" placeholder="First name and last name ..." />
<label>Phone Number:</label>
<input type="tel" placeholder="+xx-xxx-xxxxxxx" />
For accessibility, you should always link labels and inputs, either by grouping them in the same label element, by linking them via
for
andid
attributes, or by using thearia-labelledby
attribute.
For checkboxes and radio inputs, make sure you wrap the content of the label inside another element (such as a <span/>
):
<label>
<input type="checkbox" />
<span>Remember me</span>
</label>
Input Status
Use alert or status roles, or ARIA live regions, to notify the user about the status of the input (or give them some hints on how to complete it properly):
<textarea placeholder="Type something ..." maxlength="50" minlength="10"></textarea>
<div align="end" role="status">0/50</div>
You need to add the behavior separately with JavaScript:
textarea.addEventListener('input', () => { status.textContent = textarea.value.length + '/' + textarea.maxLength; })
Alerts (role="alert"
), or assertive ARIA live regions (aria-live="assertive"
) are only displayed when the
input is not focused and is invalid:
Enter a valid email address.
<!-- Using alert: -->
<label>Using alert:</label>
<input type="email" placeholder="Enter an email address" value="invalid@email."/>
<span role="alert">Enter a valid email address.</span>
<!-- Using status: -->
<label>Using status:</label>
<input type="email" placeholder="Enter an email address" value="invalid@email."/>
<span role="status">Enter a valid email address.</span>
You can also use status messages within labels:
Layout
In following sections you can find details on layout elements (containers, grids, etc.) that nokss supports, alongside example usage and customization guidelines for each.
Main Content & Sections
Wrap your main content in <main>
tag:
<main>
<h1>My Main Content</h1>
<p>My main content goes here.</p>
</main>
Main content is centered with a relative width by default, which falls back to full width on smaller screens, with a small margin. You can configure these via the following CSS variables:
Variable | Default | Description |
---|---|---|
--main-content |
60vw |
Width of main content |
768px |
Screens smaller than 768px |
|
--main-content |
calc(var(--spacing) * 4) auto |
Margin of main content |
calc(var(--spacing) * 2) |
Screens smaller than 768px |
Sections have additional vertical spacing between them. Additionally, if a section starts with a linkable header, additional offset will be created so that when loading the page with a hash in the URL, the header has enough spacing from the top of the screen (this is set to half the distance to previous section).
Variable | Default | Description |
---|---|---|
--section |
calc(var(--spacing) * 16) |
Spacing between sections |
--section |
calc(var(--section-spacing) / 2) |
Offset of section header |
Header & Footer
Use <header>
and <footer>
elements directly in the <body>
element to create a header and footer for the entire page:
<body>
<header>
Header stuff
</header>
<main>
...
</main>
<footer>
Footer stuff
</footer>
</body>
Main header and footer are sticky by default, which can be overriden using CSS:
body > footer {
position: initial;
}
You can customize main header and footer using the following CSS variables:
Variable | Default | Description |
---|---|---|
--footer-blur |
16px |
Backdrop blur of the footer |
--footer-brightness |
var(--sidebar-brightness) |
Brightness of the footer (relative to the background) |
--footer-background |
var(--background-color) |
Background of the footer |
--footer-background |
0.65 |
Background opacity of the footer |
--footer-padding |
var(--spacing) |
Padding of footer content |
Variable | Default | Description |
---|---|---|
--header-blur |
var(--footer-blur) |
Backdrop blur of the header |
--header-brightness |
var(--footer-brightness) |
Brightness of the header (relative to the background) |
--header-background |
var(--footer-background) |
Background of the header |
--header-background |
var(--footer-background |
Background opacity of the header |
--header-padding |
var(--footer-padding) |
Padding of header content |
Feed & Cards
Use <article>
elements in a container with the feed
role to create cards:
<section role="feed">
<article>First thingy</article>
<article>Second thingy</article>
<article>Third thingy</article>
</section>
Use images directly in a card to display them as card banner:
<section role="feed">
<article>
<img src="https://picsum.photos/800/300" alt="random image"/>
<h2>Card Title</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
<footer align="end">
<div>
<button>Learn More</button>
</div>
</footer>
</article>
</section>
Card Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Use <header>
to create a card header:
<section role="feed">
<article>
<header>
<h2>Card Title</h2>
</header>
<img src="https://picsum.photos/800/300" alt="random image"/>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
<footer align="end">
<div>
<button>Learn More</button>
</div>
</footer>
</article>
</section>
Card Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Use <address>
for attributing the card to some author:
<section role="feed">
<article>
<header>
<address>
<img src="https://some.avatar" alt="random avatar"/>
<div>
John Doe<br>
<small>Writes about stuff</small>
</div>
</address>
</header>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
<footer>
<div role="toolbar" align="end">
<button aria-label="like">❤</button>
<button aria-label="comment">💬</button>
<button aria-label="share">↥</button>
</div>
</footer>
</article>
</section>
Writes about stuff
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Note how in the above example, the <footer>
is used, with a toolbar within it, to provide a set of actions for the card.
You can also just give the footer element the toolbar role:
<section role="feed">
<article role="comment">
<header>
<address>
<img src="https://randomuser.me/api/portraits/women/95.jpg" loading="lazy" alt="random avatar"/>
Jeanette Martin
</address>
</header>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
<footer role="toolbar">
<button aria-label="like" >❤ 33k</button>
<button aria-label="comment">💬 1.2k</button>
<button aria-label="repost">🔁 1,024</button>
<button aria-label="share">↥</button>
</footer>
</article>
</section>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Put a toolbar at the beginning of the card to provide top-side actions:
<section role="feed">
<article>
<menu role="toolbar">
<button aria-label="like" class="icon">❤</button>
<button aria-label="comment" class="icon">💬</button>
<button aria-label="share" class="icon">↥</button>
</menu>
<img src="https://picsum.photos/800/450" alt="random image"/>
<address>
<img src="https://some.avatar" alt="random avatar"/>
Jane Smith
</address>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
</article>
</section>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Alternatively, you can put the toolbar in the header:
<section role="feed">
<article>
<header>
<address>
<img src="https://some.avatar" alt="random avatar"/>
<div>
Max Mustermann <br>
<small>General Enthusiast</small>
</div>
</address>
<menu role="toolbar">
<details>
<summary role="button" aria-label="more">⋮</summary>
<div>
<menu role="toolbar" aria-orientation="vertical">
<button>Share</button>
<button>Remove</button>
<button>Report</button>
</menu>
</div>
</details>
</menu>
</header>
<img src="https://picsum.photos/800/400" alt="random image"/>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
</article>
</section>
General Enthusiast
⋮
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Set aria-orientation
attribute on the feed to indicate the direction of the feed:
<section role="feed" aria-orientation="horizontal">
<article>
<img src="https://picsum.photos/400" alt="random image"/>
<h3>Lorem Ipsum</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit</p>
<footer>
<button>Learn More</button>
</footer>
</article>
<article>...</article>
<article>...</article>
<article>...</article>
</section>
Lorem Ipsum
Lorem ipsum dolor sit amet, consectetur adipiscing elit
Eiusmod Tempor
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Consectetur
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Magna Aliqua
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Use aria-busy
attribute to indicate that the feed is loading:
<section role="feed" aria-busy="true">
...
</section>
W 6th St
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Belt Line Rd
Ut enim ad minim veniam, quis nostrud exercitation ullamco ...
Groveland Terrace
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Customization
Tweak global CSS variables for customizing modals. If you need further customization, you can use following CSS variables:
Variable | Default | Description |
---|---|---|
--card-shadow |
0 1px 3px 0 rgba(0 0 0 / 10%) (light)0 2px 6px 0 rgba(0 0 0 / 50%) (dark)
|
Shadow of a card. |
--card-brightness |
1 (light)calc(1 - (1 - (dark)
|
Brightness card's background (relative to background). |
--raised-card |
translateY(-2px) |
Transformation of a raised card. |
--raised-card |
0 3px 9px 0 rgba(0 0 0 / 15%) (light)0 6px 18px 0 rgba(0 0 0 / 55%) (dark)
|
Shadow of a raised card. |
--raised-card |
calc(1 - (1 - (light)calc(1 - (1 - (dark)
|
Brightness of a raised card's background (relative to background). |
--horizontal-feed |
256px |
Width of a card in horizontal feed. |
Modals
Use <dialog>
element to create and display a modal:
<button onclick="document.querySelector('#modal-1').showModal()">Show Modal</button>
<dialog id="modal-1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
<footer>
<form method="dialog" align="end" role="group">
<button>Whatever</button>
<button>Got It</button>
</form>
</footer>
</dialog>
Make sure you use the
.showModal()
method for opening the dialog. Checkout MDN's guides to learn more about the<dialog>
element.
Use <header>
and <footer>
elements to add a header and footer to the modal.
<dialog>
<header><h2>Modal with header</h2></header>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
<footer>
<form method="dialog" align="end">
<button>Close</button>
</form>
</footer>
</dialog>
Modal Animations
Set --modal-animation
property to change the animation of the modal. This needs to be the name of the animation you want to use. nokss provides some animations out of the box, which you can try in the example below:
#my-modal {
--modal-animation: grow-in;
}
<dialog id="my-modal">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
<footer role="group" align="end">
<button onclick="document.querySelector('#grow-modal').close()">
Ok
</button>
</footer>
</dialog>
Customization
Tweak global CSS variables for customizing modals. If you need further customization, you can use following CSS variables:
Variable | Default | Description |
---|---|---|
--modal-width |
512px |
Width of the modal. |
--modal |
8px |
Blurring behind modal's backdrop |
--modal |
rgba(0 0 0 / 50%) |
Background of modal's backdrop |
--modal |
2 |
Brightness of the modal's background, relative to page's background. |
--modal |
grow-in |
The animation to display when a modal is opened. Can be the name of any custom defined animation. See animations section for mor edetails. |
Customization Note
Dialog backdrop DOES NOT inherit root scope variables. Override modal related variables for
:root
AND::backdrop
:
:root, ::backdrop { --modal-backdrop-background: rgba(128 128 128 / 25%); --modal-backdrop-blur: 32px; }