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
- Host: GitHub
- URL: https://github.com/tmol4/star4
- Owner: tmol4
- License: mit
- Created: 2024-10-16T17:26:08.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2024-11-24T05:11:11.000Z (6 months ago)
- Last Synced: 2025-04-04T11:24:51.362Z (about 2 months ago)
- Topics: design-system, frontend, jsx, material, material-design, material-you, material3, react, solidjs, ui, web
- Language: TypeScript
- Homepage: https://tmol4.github.io/star4/
- Size: 1.9 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
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 fileexport 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 fileimport { 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 fileimport { 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 futureThe `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).