An open API service indexing awesome lists of open source software.

https://github.com/tmol4/star4

Our official design system and UI library
https://github.com/tmol4/star4

design-system frontend jsx material material-design material-you material3 react solidjs ui web

Last synced: 30 days ago
JSON representation

Our official design system and UI library

Awesome Lists containing this project

README

        

**English** · [Русский](./README.ru.md)

> [!WARNING]
> Since the library is in active development, many developer-friendly and organizational features are not yet present, for example:
> - **Documentation website**:\
> *It takes great time to build a docs website for a multi-framework library, you know?*
> - **Documentation comments**:\
> *Unstable APIs may change frequently, so we would have to rewrite TypeDoc comments without a break.*
> - **Examples**:\
> *Projects in the [examples](examples) directory are actively used during development, but they do not yet serve as actual example projects.*
> - **Developer workflow**



star4


Our official design system and UI library


[~~Documentation~~](https://tmol4.github.io/star4) · [Report a bug](https://github.com/tmol4/star4/issues) · [Request a feature](https://github.com/tmol4/star4/issues)

Table of contents

- [Features](#features)
- [Usage](#usage)
- [Installation](#installation)
- [Theming](#theming)
- [Creating a theme](#creating-a-theme)
- [Applying styles](#applying-styles)
- [Color schemes](#color-schemes)
- [Fonts](#fonts)
- [Components](#components)
- [Identifiable components (React only)](#identifiable-components-react-only)
- [Acknowledgements](#acknowledgements)
- [Contributing](#contributing)
- [Working group](#working-group)
- [License](#license)

## Features

- 🧰 Multi-framework support
- [React](packages/react/README.md)
- [SolidJS](packages/react/README.md)
- 🎨 Design based on [Material Design 3](https://m3.material.io "m3.material.io")
- 🧱 Primitive and complex components
- 🧩 Modular structure

## Usage

### Installation

1. Add the Vanilla Extract plugin to the project\
\
The library can be used in any project which supports [Vanilla Extract](https://vanilla-extract.style "vanilla-extract.style"). All available integrations can be found at [*"Bundler Integration"*](https://vanilla-extract.style/documentation/getting-started/#bundler-integration "vanilla-extract.style/documentation/getting-started#bundler-integration"). Here's a list of currently supported integrations *(at the moment of writing)*:
- [Astro](https://vanilla-extract.style/documentation/integrations/astro "vanilla-extract.style/documentation/integrations/astro") (via `@vanilla-extract/vite-plugin`)
- [esbuild](https://vanilla-extract.style/documentation/integrations/esbuild "vanilla-extract.style/documentation/integrations/esbuild") (via `@vanilla-extract/esbuild-plugin`)
- [Gatsby](https://vanilla-extract.style/documentation/integrations/gatsby "vanilla-extract.style/documentation/integrations/gatsby") (via `gatsby-plugin-vanilla-extract`)
- [Next.js](https://vanilla-extract.style/documentation/integrations/next "vanilla-extract.style/documentation/integrations/next") (via `@vanilla-extract/next-plugin`)
- [Parcel](https://vanilla-extract.style/documentation/integrations/parcel "vanilla-extract.style/documentation/integrations/parcel") (via `@vanilla-extract/parcel-transformer`)
- [Remix](https://vanilla-extract.style/documentation/integrations/remix "vanilla-extract.style/documentation/integrations/remix") (via `@vanilla-extract/vite-plugin`)
- [Rollup](https://vanilla-extract.style/documentation/integrations/rollup "vanilla-extract.style/documentation/integrations/rollup") (via `@vanilla-extract/rollup-plugin`)
- [Vite](https://vanilla-extract.style/documentation/integrations/vite "vanilla-extract.style/documentation/integrations/vite") (via `@vanilla-extract/vite-plugin`)
- [Webpack](https://vanilla-extract.style/documentation/integrations/webpack "vanilla-extract.style/documentation/integrations/webpack") (via `@vanilla-extract/webpack-plugin`)
1. Add the Vanilla Extract core library ([`@vanilla-extract/css`](https://npmjs.com/package/@vanilla-extract/css)):

npm

```sh
npm install @vanilla-extract/css
```


pnpm

```sh
pnpm add @vanilla-extract/css
```


Yarn

```sh
yarn add @vanilla-extract/css
```


Bun

```sh
bun add @vanilla-extract/css
```

1. Install star4's Vanilla Extract utility package ([`@star4/vanilla-extract`](https://npmjs.com/package/@star4/vanilla-extract)):

npm

```sh
npm install @star4/vanilla-extract
```


pnpm

```sh
pnpm add @star4/vanilla-extract
```


Yarn

```sh
yarn add @star4/vanilla-extract
```


Bun

```sh
bun add @star4/vanilla-extract
```

1. Install star4's framework package
- React ([`@star4/react`](https://npmjs.com/package/@star4/react)):

npm

```sh
npm install @star4/react
```


pnpm

```sh
pnpm add @star4/react
```


Yarn

```sh
yarn add @star4/react
```


Bun

```sh
bun add @star4/react
```

- Solid ([`@star4/solid`](https://npmjs.com/package/@star4/solid)):

npm

```sh
npm install @star4/solid
```


pnpm

```sh
pnpm add @star4/solid
```


Yarn

```sh
yarn add @star4/solid
```


Bun

```sh
bun add @star4/solid
```

### Theming

In order to start using star4, you must first setup a theme. This can be done using the [`@star4/vanilla-extract`](https://npmjs.com/package/@star4/vanilla-extract) package.

#### Creating a theme

A theme object can be created in an ordinary (non `.css.*`) file using the `createTheme` helper function:
```ts
import { createTheme } from "@star4/vanilla-extract";

export const { contract, theme } = createTheme({/* ... */});
```

This function accepts an *options* argument, which is an object specifying theme configuration. Some of its fields are required. Here's a minimalist example:
```ts
import { createTheme } from "@star4/vanilla-extract";

export const { contract, theme } = createTheme({
// Use a static color scheme
color: {},
// Specify font families for different font styles
typeface: {
plain: "system-ui",
brand: "sans-serif",
},
// Specify the font family used for Material Symbols icons
component: {
materialSymbol: {
font: "Material Symbols Outlined",
},
},
});
```

#### Applying styles

The `createTheme` helper returns an object containing two functions: `contract` and `theme`.

First, create a theme contract using the `contract` helper:
```ts
// THIS EXAMPLES ASSUMES:
// - a star4 theme was created
// - `contract` was imported into this file

export const THEME = contract();
```

The `theme` function returns an object filled with CSS values, ready to be applied using ([`@vanilla-extract/css`](https://npmjs.com/package/@vanilla-extract/css)) inside of a `.css.*` file. You can use any Vanilla Extract API, depending on your needs.

Example creating a theme which follows system preference, using [`globalStyle`](https://vanilla-extract.style/documentation/global-api/global-style "vanilla-extract.style/documentation/global-api/global-style") and [`assignVars`](https://vanilla-extract.style/documentation/api/assign-vars "vanilla-extract.style/documentation/api/assign-vars") APIs:
```ts
// THIS EXAMPLES ASSUMES:
// - a star4 theme was created
// - a theme contract named `THEME` was created
// - `theme` helper and `THEME` were imported into this file

import { assignVars, globalStyle } from "@vanilla-extract/css";

const LIGHT_THEME = theme("light");
const DARK_THEME = theme("dark");

globalStyle(
":root, ::backdrop",
{
colorScheme: "light dark",
"@media": {
"(prefers-color-scheme: light)": {
vars: assignVars(THEME, LIGHT_THEME),
},
"(prefers-color-scheme: dark)": {
vars: assignVars(THEME, DARK_THEME),
},
},
},
);
```
Here is a more complicated example, allowing overriding the current theme mode using a data attribute:
```ts
// THIS EXAMPLES ASSUMES:
// - a star4 theme was created
// - a theme contract named `THEME` was created
// - `theme` helper and `THEME` were imported into this file

import { assignVars, globalStyle } from "@vanilla-extract/css";

// A helper function for creating attribute selectors
const createThemeSelector = (
themes: string | string[],
not: boolean = false,
) => {
const values = typeof themes === "string" ? [themes] : themes;
const attributes = values.map(
theme => `[data-theme="${theme}"]`,
);
let is = `:is(${attributes.join(",")})`;
if(not) is = `:not(${is})`;
return `:root${is}, ${is} ::backdrop`;
}

// Store filled theme objects for reuse
const LIGHT_THEME = theme("light");
const DARK_THEME = theme("dark");

// Possible data-theme attribute values
const DATA_THEME_LIGHT = "light";
const DATA_THEME_DARK = "dark";

// Follow system theme mode if no
// [data-theme="light"] or [data-theme="dark"]
// attribute was found
globalStyle(
createThemeSelector([DATA_THEME_LIGHT, DATA_THEME_DARK], true),
{
colorScheme: "light dark",
"@media": {
"(prefers-color-scheme: light)": {
vars: assignVars(THEME, LIGHT_THEME),
},
"(prefers-color-scheme: dark)": {
vars: assignVars(THEME, DARK_THEME),
},
},
},
);

// If has a [data-theme="light"]
// attribute, apply the light theme
globalStyle(
createThemeSelector(DATA_THEME_LIGHT),
{
colorScheme: "light",
vars: assignVars(THEME, LIGHT_THEME),
}
);

// If has a [data-theme="dark"]
// attribute, apply the dark theme
globalStyle(
createThemeSelector(DATA_THEME_DARK),
{
colorScheme: "dark",
vars: assignVars(THEME, DARK_THEME),
}
);
```

#### Color schemes

star4 uses the [Material You color system](https://m3.material.io/styles/color/system "m3.material.io/styles/color/system"), which provides two options for creating color schemes: [**static**](https://m3.material.io/styles/color/static/baseline "m3.material.io/styles/color/static/baseline") and [**dynamic**](https://m3.material.io/styles/color/dynamic "m3.material.io/styles/color/dynamic"). See [*Choosing a scheme*](https://m3.material.io/styles/color/choosing-a-scheme "m3.material.io/styles/color/choosing-a-scheme") for advice.

Static color scheme


Light







Dark


> [!WARNING]
> The behaviour of the `color` property might change in the future

The `color` property is responsible for color scheme configuration. Settings it to an empty object `{}` will assert a static scheme, while creating a dynamic scheme requires specifying a few properties.

Example of a static color scheme:
```ts
export const { contract, theme } = createTheme({
// Empty object - using a static color scheme
color: {},
/* ... other fields... */
});
```
Example of a dynamic color scheme:
```ts
export const { contract, theme } = createTheme({
color: {
// A total of 9 variants are available
variant: "tonalSpot"
// Can be a hex, rgb CSS color, or an Hct instance
sourceColor: "#00ff00",
// Defaults to 0. Allows changing color scheme contrast
contrastLevel: 0,
},
/* ... other fields... */
});
```

#### Fonts

Font family names containing spaces must be quoted in CSS.

Here is a helper function for merging CSS font names:
```ts
type FontFamily = (string | FontFamily)[];

const fontFamily = (...args: FontFamily): string => {
return args
.map(
value => typeof value === "string"
? value.includes(" ")
? `"${value}"`
: value
: fontFamily(...value),
)
.join(", ");
}
```

If using variable fonts from [FontSource](https://fontsource.org "fontsource.org"), their font family names have ` Variable` appended to them, e.g. `Open Sans Variable`, so here is a helper for FontSource fonts:
```ts
// Use system font if available,
// otherwise load the FontSource font
const fontSource = (family: string) => {
return [`${family}`, `${family} Variable`];
};
```
Here's an example showcasing both helpers in action:

```ts
// Gets transformed to:
// "Roboto Flex", "Roboto Flex Variable", "Open Sans", "Open Sans Variable", Roboto, system-ui, Arial, sans-serif
const TYPEFACE_PLAIN = fontFamily(
fontSource("Roboto Flex"),
fontSource("Open Sans"),
"Roboto",
"system-ui",
"Arial",
"sans-serif",
);

// Gets transformed to:
// Raleway, "Raleway Variable", Manrope, "Manrope Variable", sans-serif
const TYPEFACE_BRAND = fontFamily(
fontSource("Raleway"),
fontSource("Manrope"),
"sans-serif",
);
export const { contract, theme } = createTheme({
typeface: {
plain: TYPEFACE_PLAIN,
brand: TYPEFACE_BRAND,
},
/* other fields */
});

```

### Components

star4 framework packages export all available components, most of them contain documentation comments and code samples, so documentation for each component will not be provided here.

#### Identifiable components (React only)

The [`@star4/react`](https://npmjs.com/package/@star4/react) package provides an additional helper with each component: `.is()`. This is a function which can be used to identify if a React element is a specific element. It is useful for filtering out children elements:
```tsx
import { Children, type ReactNode } from "react";
import { Button } from "@star4/react";

// This component only displays buttons
// Warns when other elements are passed as children
export function OnlyButtons({ children }: { children: ReactNode }) {
const buttons = Children.toArray(children)
.filter(node => {
const is = Button.is(node);
if(!is) console.warn(
"Invalid JSX Element passed to token resolver:",
node,
);
return is;
});

return buttons;
}
```

> [!WARNING]
> Name and return value of the `createIdentifiableElement` function might change in the future.

Making your own components identifiable is also possible via the `createIdentifiableElement` helper. We advice you to follow the naming convention shown in the example:
```tsx
import { createIdentifiableElement } from "@star4/react";

// forwardRef may also be used instead of direct assignment
const ExampleComponent = function Example() {

}

export const Example = Object.assign(
// memo(ExampleComponent) may also be used for memoization
ExampleComponent,
// IS_EXAMPLE is the description of
// a Symbol created to identify the element
createIdentifiableElement("IS_EXAMPLE"),
);

// Use the component

// Identify the element
Example.is(something)
```

## Acknowledgements

star4 was only possible because of the following amazing projects:

- [**Solid Primitives**](https://primitives.solidjs.community "primitives.solidjs.community") - [GitHub](https://github.com/solidjs-community/solid-primitives/tree/main/packages/active-element)\
The swiss army knife for SolidJS.

- [**Material Web**](https://material-web.dev "material-web.dev") - [GitHub](https://github.com/solidjs-community/solid-primitives/tree/main/packages/active-element)\
Official Material Design 3 web components library.

- [**usehooks-ts**](https://usehooks-ts.com "usehooks-ts.com") - [GitHub](https://github.com/juliencrn/usehooks-ts)\
Awesome React hooks library.

## Contributing

See [**CONTRIBUTING.md**](CONTRIBUTING.md).

### Working group

- [@deminearchiver](https://github.com/deminearchiver)

## License

This project is licensed under the [**MIT License**](LICENSE).