Lightweight Material Design 3 CSS Framework – Beer CSS

Category: Frameworks , Javascript | July 31, 2026
Authorbeercss
Last UpdateJuly 31, 2026
LicenseMIT
Views30 views
Lightweight Material Design 3 CSS Framework – Beer CSS

Beer CSS is a CSS and JavaScript framework that styles semantic HTML elements to match Google’s Material Design 3 specification, including the newer “M3 Expressive” variant.

You write a <button>, a <dialog>, or a <nav> with a handful of class names, and the framework handles the styles: elevation, ripple feedback, color roles, and responsive spacing.

The framework organizes its class system into three groups it calls settings, elements, and helpers.

Settings apply once at the document level (light or dark mode, for example). Elements are the components: buttons, cards, chips, dialogs, navigation, and roughly two dozen more. Helpers are small modifier classes, such as small, border, or center-align, that combine with any element to adjust its size, shape, spacing, or color.

Beer CSS ships as a compiled CSS and JS bundle with zero required dependencies, and it works alongside any JavaScript framework since it targets standard HTML tags.

A separate material-dynamic-colors package adds runtime theme generation from a color, an image, or an uploaded file, useful for apps that let users pick their own accent color instead of shipping one fixed palette.

Features:

  • Material Design 3 and M3 Expressive UI styling.
  • Semantic HTML elements paired with compact helper classes.
  • Responsive grids, content containers, navigation rails, and bottom bars.
  • Components for forms, data display, menus, dialogs, feedback, and navigation.
  • Light, dark, automatic, and runtime-generated color themes.
  • Default, scoped, and custom-element distributions.
  • Theme tokens for palettes, surfaces, typography, elevation, sizing, and motion.
  • Helper classes for spacing, alignment, color, shape, visibility, and responsive behavior.
  • Optional JavaScript for state toggling, dynamic themes, sliders, textarea resizing, and icon optimization.
  • ES module and npm support for modern front-end projects.

How To Use It

Basic Usage

Load the default stylesheet and JavaScript module from a CDN. The JavaScript file activates data-ui triggers, while the stylesheet handles the component appearance.

<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/beercss/dist/cdn/beer.min.css"
>
<script
  type="module"
  src="https://cdn.jsdelivr.net/npm/beercss/dist/cdn/beer.min.js">
</script>
<main class="responsive">
  <article class="large-padding round">
    <h5>Release approval</h5>
    <p>Review the deployment summary before publishing.</p>
    <nav>
      <button class="border">View logs</button>
      <button data-ui="#approval-dialog">Approve release</button>
    </nav>
  </article>
</main>
<dialog id="approval-dialog">
  <h5>Approve this release?</h5>
  <p>The production deployment will start immediately.</p>
  <nav class="right-align">
    <button class="transparent" data-ui="#approval-dialog">Cancel</button>
    <button data-ui="#approval-dialog">Confirm</button>
  </nav>
</dialog>

The data-ui attribute targets the dialog by CSS selector. Beer CSS toggles its active helper when either button runs the trigger.

Install with npm

Install the main package and the dynamic color package:

npm install beercss material-dynamic-colors

Import Beer CSS in the application entry file. Import Material Dynamic Colors only when the project generates palettes at runtime.

import "beercss";
// Required for ui("theme", ...).
import "material-dynamic-colors";

The package also exposes scoped and custom-element entry points:

// Limit Beer CSS to descendants of an element with class="beer".
import "beercss/scoped";
// Apply Beer CSS inside <beer-css>.
import "beercss/custom-element";

Choose one Beer CSS entry point for the page or application.

Build a Responsive App Shell

Beer CSS uses s, m, and l helpers to target small, medium, and large layouts. Separate navigation elements let the interface adopt a bottom bar on small screens and a side rail on wider screens.

<nav class="left max l">
  <a class="active">
    <i>dashboard</i>
    <span>Dashboard</span>
  </a>
  <a>
    <i>inventory_2</i>
    <span>Inventory</span>
  </a>
  <a>
    <i>settings</i>
    <span>Settings</span>
  </a>
</nav>
<nav class="left m">
  <a class="active"><i>dashboard</i></a>
  <a><i>inventory_2</i></a>
  <a><i>settings</i></a>
</nav>
<nav class="bottom s">
  <a class="active"><i>dashboard</i></a>
  <a><i>inventory_2</i></a>
  <a><i>settings</i></a>
</nav>
<main class="responsive">
  <h3>Inventory dashboard</h3>
  <div class="grid">
    <div class="s12 l6">
      <article class="large-padding">Stock levels</article>
    </div>
    <div class="s12 l6">
      <article class="large-padding">Recent orders</article>
    </div>
  </div>
</main>

Material Symbols render the text inside each <i> element. Replace those elements with SVG or image icons when the project uses another icon source.

Toggle Dialogs, Menus, Pages, Overlays, and Snackbars

The ui() helper and data-ui attribute share the same trigger model. They add or remove the active helper on the selected element.

// Toggle a dialog, menu, overlay, page, or similar component.
ui("#preferences-dialog");
// Show a snackbar and hide it after 4 seconds.
ui("#saved-snackbar", 4000);

HTML attributes handle direct click triggers:

<button data-ui="#account-menu">Open account menu</button>
<menu id="account-menu">
  <li>Profile</li>
  <li>Billing</li>
  <li>Sign out</li>
</menu>
<button id="save-settings">Save</button>
<div class="snackbar" id="saved-snackbar">
  <span>Settings saved.</span>
</div>
<script type="module">
  document.querySelector("#save-settings").addEventListener("click", () => {
    ui("#saved-snackbar", 4000);
  });
</script>

Native browser APIs also remain available. A <dialog> works with show(), showModal(), and close(), while a snackbar or menu can use the Popover API when that path fits the project.

Set Light, Dark, and Automatic Modes

Leave the <body> class empty to follow the device color scheme. Add light or dark to force the initial mode.

<body>
  <!-- Follows the device color scheme. -->
</body>
<body class="light">
  <!-- Always starts in light mode. -->
</body>
<body class="dark">
  <!-- Always starts in dark mode. -->
</body>

The JavaScript helper reads or changes the mode at runtime:

// Read the current mode.
const currentMode = ui("mode");
// Set a fixed mode.
ui("mode", "light");
ui("mode", "dark");
// Return to the device preference.
ui("mode", "auto");

Generate a Dynamic Material Color Theme

Load Material Dynamic Colors when the interface needs a palette generated from a seed color or visual asset.

<script
  type="module"
  src="https://cdn.jsdelivr.net/npm/material-dynamic-colors/dist/cdn/material-dynamic-colors.min.js">
</script>
<script type="module">
  document
    .querySelector("#apply-brand-theme")
    .addEventListener("click", async () => {
      await ui("theme", "#00639b");
    });
</script>
<button id="apply-brand-theme">Apply brand theme</button>

Customize Theme Tokens and Helper Classes

Override Beer CSS variables after the framework stylesheet. Define separate light and dark values when the project supports both modes.

-root,
body.light {
  --primary: #005ac1;
  --on-primary: #ffffff;
  --primary-container: #d8e2ff;
  --on-primary-container: #001a41;
  --surface: #f9f9ff;
  --on-surface: #1a1b20;
  --outline: #74777f;
  --font: "Inter", sans-serif;
  --speed2: 0.18s;
}
body.dark {
  --primary: #adc6ff;
  --on-primary: #002e69;
  --primary-container: #004494;
  --on-primary-container: #d8e2ff;
  --surface: #111318;
  --on-surface: #e2e2e9;
  --outline: #8e9099;
}

The main token groups include primary, secondary, tertiary, error, background, surface, outline, inverse, elevation, font, size, and animation speed values.

Helper classes handle common component adjustments:

<article class="large-padding round medium-elevate">
  <h5 class="primary-text">Monthly usage</h5>
  <p class="medium-opacity">Updated five minutes ago.</p>
  <nav class="right-align">
    <button class="border">Export</button>
    <button>Open report</button>
  </nav>
</article>

Alternatives:

FAQs:

Q: Does Beer CSS require JavaScript?
A: Static component styling works from CSS and HTML. Load beer.min.js for the ui() helper, data-ui triggers, slider updates, cross-browser textarea resizing, and related JavaScript-assisted behavior.

Q: Can Beer CSS work inside React, Vue, Svelte, or another framework?
A: Yes. Its browser-facing API uses HTML, CSS classes, and ES modules.

Q: How do I stop Beer CSS from styling the entire page?
A: Load beer.scoped.min.css and wrap the target interface in an element with class="beer".

Q: Why does ui("theme", value) fail with an image URL?
A: The dynamic theme call needs material-dynamic-colors, and remote images must permit cross-origin access. Use a local file, blob, same-origin path, or a server with the correct CORS headers.

Q: Does Beer CSS make an interface accessible automatically?
A: Beer CSS favors semantic elements and native controls, but application code still owns labels, focus order, dialog behavior, keyboard interaction, contrast checks, and assistive text.

Changelog:

v5.0 (07/31/2026)

  • Updated icon fonts
  • Added support for modules. The bundle can be reduced by up to 10x
  • Bugfixes

You Might Be Interested In:


Leave a Reply