Kosmesis
Components

Progress

A progress bar with an indeterminate mode, wrapping Morphos's Progress primitive.

Installation

npx kosmesis add progress
pnpm dlx kosmesis add progress
yarn dlx kosmesis add progress
bunx kosmesis add progress

Install the following dependencies:

npm install @morphos/feedback
pnpm add @morphos/feedback
yarn add @morphos/feedback
bun add @morphos/feedback

Copy and paste the following code into your project.

progress.tsx
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import { Progress as MorphosProgress, type ProgressProps as MorphosProgressProps  } from "@morphos/feedback";import { cn } from "@/lib/utils";export type ProgressProps = MorphosProgressProps;/** * Morphos's `Progress` sets `role="progressbar"` and the `--progress` custom property on the * same root element (it does not render children), so the ARIA element doubles as the visual * fill here — wrapped in a track `div` for the background/rounding. */@Component()export class Progress extends StatelessComponent<ProgressProps> {  render() {    const { class: cls, ...rest } = this.props;    return (      <div class={cn("relative h-2 w-full overflow-hidden rounded-full bg-primary/20", cls)}>        <MorphosProgress          class="block h-full w-(--progress,0%) origin-left bg-primary transition-all data-indeterminate:w-1/3 data-indeterminate:animate-pulse"          {...rest}        />      </div>    );  }}

Install the following dependencies:

npm install @morphos/feedback @praxisjs/css
pnpm add @morphos/feedback @praxisjs/css
yarn add @morphos/feedback @praxisjs/css
bun add @morphos/feedback @praxisjs/css

Copy and paste the following code into your project.

progress.tsx
import { StatelessComponent } from "@praxisjs/core";import { cx, keyframes, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import { Progress as MorphosProgress, type ProgressProps as MorphosProgressProps  } from "@morphos/feedback";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);const pulse = keyframes("kosmesis-pulse", { "0%, 100%": { opacity: "1" }, "50%": { opacity: "0.5" } });class ProgressStyles extends Stylesheet {  $track = this.css({    position: "relative",    height: "0.5rem",    width: "100%",    overflow: "hidden",    borderRadius: "9999px",    backgroundColor: `color-mix(in oklab, ${t.primary} 20%, transparent)`,  });  $fill = this.css({    display: "block",    height: "100%",    width: "var(--progress, 0%)",    transformOrigin: "left",    backgroundColor: t.primary,    transition: "width 200ms ease",  }).on("&[data-indeterminate]", { width: "33%", animation: `${pulse} 2s cubic-bezier(0.4, 0, 0.6, 1) infinite` });}export type ProgressProps = MorphosProgressProps;/** * Morphos's `Progress` sets `role="progressbar"` and the `--progress` custom property on the * same root element (it does not render children), so the ARIA element doubles as the visual * fill here — wrapped in a track `div` for the background/rounding. */@Component()export class Progress extends StatelessComponent<ProgressProps> {  @Styled(ProgressStyles) $s!: ProgressStyles;  render() {    const { class: cls, ...rest } = this.props;    return (      <div class={cx(this.$s.$track, cls)}>        <MorphosProgress class={this.$s.$fill} {...rest} />      </div>    );  }}

Examples

Usage

import { Progress } from "@/components/ui/progress";

<Progress value={66} />
<Progress /> {/* indeterminate — no value prop */}

Props

PropTypeDefault
valuenumber | undefined— (indeterminate when omitted)
max / minnumber100 / 0

Morphos's Progress sets role="progressbar" and the --progress custom property on its own root element rather than rendering a separate indicator child — Kosmesis wraps it in a track div for the background, and the fill itself is the Morphos element, sized via w-(--progress,0%).

On this page