Installation
How to install and set up Fasla in your project.
Requirements
- React 18 or later
- Tailwind CSS 3.4 or later
- TypeScript (recommended)
Using the CLI
The easiest way to add components is the CLI in the @smicolon/fasla-ui package. It takes two steps.
1. Initialize Fasla, once per project. This writes components.json, which every add reads:
npx @smicolon/fasla-ui@latest initSet up with @smicolon/cli? If it was 0.4 or later, keep going with this command in the same project. Nothing needs re-running. If it was 0.3 or earlier, run npx @smicolon/fasla-ui@latest init once first. It repairs the project’s components.json, which the shadcn CLI can’t read until then, and moves the files those versions put in the wrong folder.
init also writes the cn helper and installs the two packages it imports with your project’s package manager. If that install fails, init prints the command to run, which is one of these:
npm install clsx tailwind-mergeOn Tailwind 3, the command installs tailwind-merge@^2, as tailwind-merge 3 supports only Tailwind 4. init and add also replace a tailwind-merge that is, or could become, 3:
npm install clsx tailwind-merge@^22. Add a component:
npx @smicolon/fasla-ui@latest add buttonOr add several at once:
npx @smicolon/fasla-ui@latest add card input badgeadd then installs the packages the components import, with your project’s package manager, and skips any your project already has at a compatible version. If that install fails, add keeps the files it wrote and prints the command to run. To print the command instead of installing, add --no-install.
List everything the registry offers:
npx @smicolon/fasla-ui@latest listWithout questions: for agents and CI
init asks questions only when a terminal is there to answer them. Run without one, as an agent or a CI job does, it never waits: pass --yes to accept every default, or pass all three of --path, --style and --theme. When a choice is missing, init lists the flags to pass, writes nothing and exits with an error.
--yes(-y) accepts every default and asks nothing.--pathis the folder components go in, such assrc/components. The CLI puts them in auifolder under it, sosrc/components/uigives the same result.--styleis the shadcn style written tocomponents.json:defaultornew-york.--themeis the theme layer:faslafor Fasla’s colours, orbrandto keep yours.--no-installprints the install commands instead of running them.
For example:
npx @smicolon/fasla-ui@latest init --yes
npx @smicolon/fasla-ui@latest init --path src/components --style default --theme fasla
npx @smicolon/fasla-ui@latest add button --yesWhen it finishes, init lists each file it created or changed and the command to run next. add also needs --yes when there is no terminal.
Theme
Components take every colour from your theme. init asks which of two layers you want and installs it. The shadcn CLI commands below install a layer too, or switch a project to the other one later. Both give you the base layer.
The shadcn commands need a project that is already set up, with a components.json. In a new project, run npx @smicolon/fasla-ui@latest init first, or npx shadcn@latest init. Run in a project without one, the shadcn CLI sets the project up itself, and the colours of the preset it picks replace Fasla’s.
Starting from scratch: use Fasla’s colours. Fasla’s light and dark palette, its radius and the Geist font, on top of the base layer:
npx shadcn@latest add https://fasla.smicolon.com/r/ theme.json https://fasla.smicolon.com/r/ font-geist.jsonOn Next.js 14, set the project up with npx @smicolon/fasla-ui@latest init, not shadcn init: shadcn’s setup writes Tailwind 4 styles and loads Geist from next/font, and Next.js 14 has neither, so the build fails. Then leave out font-geist.json, as the Next.js 14 template already loads Geist:
npx shadcn@latest add https://fasla.smicolon.com/r/ theme.jsonI have a brand: keep my colours. The base layer only: the tokens shadcn doesn’t have (success, warning, info and the soft tints), the type scale with Arabic line heights, the Arabic setting and the Cairo font. It changes no colour you already have:
npx shadcn@latest add https://fasla.smicolon.com/r/ theme-base.jsonThe shadcn CLI asks Existing CSS variables and components will be overwritten. Continue? with No as the default, so type y. The base layer, theme-base, doesn’t change any colour your project already has.
Arabic text is set in Cairo through the --font-arabic variable. To use another Arabic font, point --font-arabic at it.
Fasla’s base components replace shadcn’s components of the same name, such as button and badge, in components/ui. add asks before it replaces a file that isn’t Fasla’s.
Inverse tokens
A block on an inverted surface, such as the FAQ Dark Panel, paints it with three tokens: background-inverse, foreground-inverse and muted-foreground-inverse. Fasla’s theme sets them: the surface is dark in light mode and light in dark mode. They are the only way Fasla inverts a surface, so a block never forces a dark class on a section or borrows bg-foreground.
Adding such a block with either CLI also adds the tokens’ mapping. When your theme doesn’t set the tokens, each falls back to your page colours, swapped: foreground for the surface, background for its text. To set your own, define --background-inverse, --foreground-inverse and --muted-foreground-inverse for light and dark.
Adding the mapping by hand on Tailwind 4: these three lines go in your stylesheet’s @theme inline block.
--color-background-inverse: var(--background-inverse, var(--foreground));
--color-foreground-inverse: var(--foreground-inverse, var(--background));
--color-muted-foreground-inverse: var(--muted-foreground-inverse, color-mix(in oklab, var(--background) 75%, var(--foreground)));On Tailwind 3, these go under theme.extend.colors in tailwind.config:
"background-inverse": "color-mix(in oklab, var(--background-inverse, theme(colors.foreground)) calc(<alpha-value> * 100%), transparent)",
"foreground-inverse": "color-mix(in oklab, var(--foreground-inverse, theme(colors.background)) calc(<alpha-value> * 100%), transparent)",
"muted-foreground-inverse": "color-mix(in oklab, var(--muted-foreground-inverse, color-mix(in oklab, theme(colors.background) 75%, theme(colors.foreground))) calc(<alpha-value> * 100%), transparent)",Manual Installation
You can also copy components directly from the source code.
1. Install dependencies:
npm install class-variance-authority clsx tailwind-merge framer-motionOn Tailwind 3, install tailwind-merge@^2 instead. tailwind-merge 3 supports only Tailwind 4, and on Tailwind 3 it drops classes it doesn’t know, such as outline:
npm install class-variance-authority clsx tailwind-merge@^2 framer-motion2. Add the cn utility to your project:
// lib/utils.ts
import { type ClassValue, clsx } from "clsx"
import { extendTailwindMerge } from "tailwind-merge"
const twMerge = extendTailwindMerge({
extend: { classGroups: { "font-size": [{ text: ["xxs", "link", "list-header"] }] } },
})
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}3. Copy each component from its file in the registry, such as https://fasla.smicolon.com/r/button.json for Button. https://fasla.smicolon.com/r/registry.json lists them all.
In each file, files holds the source in content and the path to save it at in target. dependencies lists the packages to install, and registryDependencies the other Fasla components it needs, which you copy the same way.