GitHub ↗

skin-material

A Material Design skin for Fluixi UI, activate with data-ui="material".


Restyles the components to Material 3: pill buttons, the baseline palette in light and dark, tonal surfaces that rise by colour as well as by shadow, filled text fields with an underline, a state layer on every interactive element, and self-hosted Roboto. It composes with the data-theme and data-accent axes: an accent other than blue replaces the M3 primary, and high-contrast keeps the token palette.

Installation

pnpm add @fluixi-ui/skin-material

Usage

Import the stylesheet after the tokens + preset, then set data-ui="material":

import '@fluixi-ui/styles/all.css';
import '@fluixi-ui/skin-material/styles.css';

document.documentElement.dataset.ui = 'material';

Or drive it reactively, lazy-loading the CSS only when the skin is active:

import { createTheme } from '@fluixi/core';

const skin = createTheme({
  attribute: 'data-ui',
  themes: ['fluixi', 'material'],
  default: 'fluixi',
  load: (s) => s === 'material' && import('@fluixi-ui/skin-material/styles.css'),
});
skin.init();

The ripple

The CSS state layer covers hover / focus / press, but a true click-origin ripple needs the pointer position, so it's a tiny JS primitive. Call installRipple once; it delegates a single pointerdown listener that spawns the ripple inside any Material control:

import { installRipple } from '@fluixi-ui/skin-material/ripple';

installRipple(); // returns a disposer

What it restyles

  • Buttons, 40px pills with a 14px medium label. solid is flat at rest and rises to level 1 on hover; soft is the tonal button, outline a transparent pill with an outline edge, ghost the text button. State shows as a currentColor layer (8% hover, 12% focus and press) and never as scale.
  • Icon buttons and FABs, 40px circles; FABs on the primary container with a 16px corner, 96px for the large size.
  • Selection controls, the 52x32 switch whose thumb grows from 16px to 24px when checked and to 28px when pressed; 18px checkbox and 20px radio; a 4px tonal slider track. Each shows a 40px state layer behind it.
  • Fields, filled by default (container fill, 4px top corners, an underline that turns 2px primary on focus), or outlined (a 4px box with a 2px primary edge on focus). 56px high. With data-ui-corner="round" a filled field becomes the search bar's capsule, with a ring inside it for focus and errors in place of the underline.
  • Menus and lists, 4px containers with full-bleed rows.
  • Surfaces, tonal elevation: cards on the low container, menus on the container, dialogs on the high container with 28px corners. Surface elevations 0 to 4 step through the container colours with their shadow level.
  • Tabs, a 3px primary indicator with a rounded top. Chips have 8px corners. Toasts are snackbars on the inverse surface.

How it works

All rules live in the flx.skin cascade layer, which sits above flx.components (the default look), so they win wherever they match, while your CSS stays unlayered and wins over both. Everything is scoped to [data-ui="material"], so the skin only applies where you opt in.


Part of the Fluixi UI component library. Made with ☕ by the Fluixi team.