npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@voraus/mui-theme

v0.3.2

Published

The voraus brand as a Material UI theme

Readme

@voraus/mui-theme

The voraus brand as a Material UI theme. This is what makes a React application look like voraus without its component code being touched.

import { ThemeProvider } from '@mui/material/styles'
import { vorausTheme } from '@voraus/mui-theme'

;<ThemeProvider theme={vorausTheme}>{children}</ThemeProvider>

vorausThemeOptions is exported beside it, for an application that extends the theme before building it.

Light and dark

The theme has a light and a dark color scheme. Both name the same tokens, and the tokens change with data-theme, so the two differ only in palette.mode, which Material UI branches on. The ThemeProvider owns that attribute: it writes data-theme="light" or "dark" onto <html>, which is the selector the dark token stylesheet is published under, and it keeps the mode in local storage as mui-mode. A toggle calls useColorScheme().setMode():

import InitColorSchemeScript from '@mui/material/InitColorSchemeScript'
import { ThemeProvider, useColorScheme } from '@mui/material/styles'
import { vorausTheme } from '@voraus/mui-theme'

function Toggle() {
  const { mode, setMode } = useColorScheme()
  // `mode` is undefined until the provider has read the stored one.
  if (!mode) return null
  return <button onClick={() => setMode(mode === 'dark' ? 'light' : 'dark')}>{mode}</button>
}

;<>
  <InitColorSchemeScript attribute="data-theme" />
  <ThemeProvider theme={vorausTheme}>
    <Toggle />
    {children}
  </ThemeProvider>
</>

InitColorSchemeScript goes into the <head> or at the top of <body>, so the attribute is set before the first paint and a page stored as dark never flashes light. Its attribute has to name the same attribute as the theme. Without a stored mode, both follow prefers-color-scheme.

Since the provider writes the attribute, it is the only thing that may. Docusaurus sets data-theme itself and renders no Material UI theme, so nothing here touches it. A host that owns the attribute and mounts this theme as well hands the provider a storageManager that answers with the host's mode. The brand platform is that case: .storybook/preview.ts in @voraus/storybook keeps its page light and shows dark in subtrees that set data-theme themselves.

The stylesheets are the consumer's to load

Every value in this theme is a var(). Nothing here loads the stylesheet that declares those custom properties, and without it Material UI renders the gradient button with no gradient, black text, square corners and Times New Roman. An application therefore imports them once, at its entry point:

import '@voraus/assets/fonts/roboto.css'
import '@voraus/tokens/voraus.css'
import '@voraus/tokens/voraus-dark.css' // only if the application offers a dark theme
import '@voraus/tokens/voraus-light.css' // only to pin a subtree to light on a dark page
import '@voraus/css/voraus.css' // only for the tile, the rule and the plain class names

This package does not import them for you, because two of them are decisions only the application can make. Without the dark stylesheet the dark scheme keeps the light values, so whether an application offers dark at all is up to it. @voraus/css carries a small reset, and a library should not put one into an existing application without being asked. It is needed for <Card variant="tile"> and <Divider variant="gradient">, which are drawn by the stylesheet rather than by this theme.

Every value is a token

palette.primary.main is var(--voraus-color-action-primary), not a hex, so this application and a Sphinx page loading @voraus/css resolve the same custom properties and switching the theme moves both.

Material UI derives light, dark and contrastText from main when they are missing, and that derivation parses the color and answers a var() with "Unsupported color". Every role therefore spells all four out, and the tests check that, because a missing one is a throw in the consuming application rather than a failure here.

light and dark are hover and active states rather than brand values, so they are mixed off the role with color-mix. The same goes for the greys Material UI would otherwise fill with rgba blacks, which vanish on a dark surface.

Material UI still fills what it is not asked about with literals of its own, such as grey, secondary and the FilledInput background. A component this theme does not style draws with those, in either mode. The tests check that the dark scheme adds no literal the light one does not already have.

One variant is added, three are inherited

text, outlined and contained are Material UI's own words, and @voraus/css uses the same three so a Sphinx page and a React app do not name the same thing twice. gradient is the addition, declared through module augmentation, so <Button variant="gradient"> is type-safe.

It uses the brand gradient, as vorausrobotik.com sets its primary button. A contained button fills with action.fill rather than primary, because primary on a dark page takes no white label.

Fields take size="small"

A TextField, a Select or an Autocomplete with size="small" is as high as control.min-height-small, which each face sets as it sets control.min-height. The resting label moves in for that height. The small field is for a dense form and for the header of a documentation, where it stands as high as the icon button beside it, so the header keeps its height.

A button keeps the full height at size="small". A card sets its actions small, and the button of @voraus/css has one height only, so a small button would break the two apart.

A multiline field grows

A field with multiline takes the height of its rows and its text. Its first line stands where the text of a single-line field does, a half of control.min-height from the top, or of control.min-height-small at size="small". One row is therefore as high as a single-line field, and the resting label sits on that line. The text keeps the inset of a single-line field.

Its corner is control.radius, but never rounder than the curve of a single-line field of the same size. A pill of three lines would turn into a stadium. The display face gets a rounded rectangle with the corners of its pill, the application face stays square.

The stepper runs on the gradient

Stepper draws the rail of the steps in @voraus/css: a point per step in the color the gradient has there, joined by the stretch of the gradient between two points. What the stepper has not reached yet is grey, so the gradient reads as the progress. The point replaces the numbered circle, because white on the turquoise end of the gradient does not reach 3:1. The number stands in front of the title instead, as in @voraus/css.

The stepper is for a flow the user works through. A history or a procedure that is only read takes VorausSteps from @voraus/react, which draws the same rail with the prose of each step under it.

The theme works the color out from the position of the step with sibling-index(), and registers the two custom properties it needs with @property, so it needs no stylesheet of @voraus/css. A browser without sibling-index() draws every point at the start of the gradient.

Tabs and the accordion match the stylesheet

The chosen tab draws its own bar rather than a sliding indicator, and the accordion is square, inside the one line and without a shadow. @voraus/css draws both the same way, and the brand platform fails a test where the two differ.

The card is square

Cards are square against the pill buttons and drawn with a line rather than a shadow, as the draft has them, so elevation: 0 is the default. In dark mode Material UI otherwise lightens a raised Paper with a white gradient image, which no token reaches. The dark scheme declares no such overlay, as the light one never had.