Kosmesis
Components

Empty

A blank/empty-state layout with header, media, title, description, and content slots. Purely presentational.

Installation

npx kosmesis add empty
pnpm dlx kosmesis add empty
yarn dlx kosmesis add empty
bunx kosmesis add empty

Copy and paste the following code into your project.

empty.tsx
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { cn } from "@/lib/utils";export interface EmptySlotProps {  class?: string;  id?: string;  children?: Children;}/** Purely presentational empty/blank-state primitives — no Morphos equivalent, same as upstream shadcn/ui. */@Component()export class Empty extends StatelessComponent<EmptySlotProps> {  render() {    const { class: cls, id, children } = this.props;    return (      <div        id={id}        class={cn("flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center", cls)}      >        {children}      </div>    );  }}@Component()export class EmptyHeader extends StatelessComponent<EmptySlotProps> {  render() {    const { class: cls, children } = this.props;    return <div class={cn("flex max-w-sm flex-col items-center gap-2 text-center", cls)}>{children}</div>;  }}@Component()export class EmptyMedia extends StatelessComponent<EmptySlotProps & { variant?: "default" | "icon" }> {  render() {    const { class: cls, variant = "default", children } = this.props;    return (      <div        class={cn(          "flex shrink-0 items-center justify-center",          variant === "icon" &&            "mb-2 size-10 rounded-lg bg-muted text-muted-foreground [&_svg]:size-5",          cls,        )}      >        {children}      </div>    );  }}@Component()export class EmptyTitle extends StatelessComponent<EmptySlotProps> {  render() {    const { class: cls, children } = this.props;    return <div class={cn("text-lg font-medium tracking-tight", cls)}>{children}</div>;  }}@Component()export class EmptyDescription extends StatelessComponent<EmptySlotProps> {  render() {    const { class: cls, children } = this.props;    return <div class={cn("text-sm text-muted-foreground [&>a]:underline [&>a]:underline-offset-4", cls)}>{children}</div>;  }}@Component()export class EmptyContent extends StatelessComponent<EmptySlotProps> {  render() {    const { class: cls, children } = this.props;    return <div class={cn("flex w-full max-w-sm flex-col items-center gap-4 text-sm", cls)}>{children}</div>;  }}

Install the following dependencies:

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

Copy and paste the following code into your project.

empty.tsx
import { StatelessComponent } from "@praxisjs/core";import { cx, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);class EmptyStyles extends Stylesheet {  $root = this.css({    display: "flex",    minWidth: "0",    flex: "1 1 0%",    flexDirection: "column",    alignItems: "center",    justifyContent: "center",    gap: "1.5rem",    borderRadius: "0.5rem",    borderStyle: "dashed",    padding: "1.5rem",    textAlign: "center",  });  $header = this.css({    display: "flex",    maxWidth: "24rem",    flexDirection: "column",    alignItems: "center",    gap: "0.5rem",    textAlign: "center",  });  $media = this.css({ display: "flex", flexShrink: 0, alignItems: "center", justifyContent: "center" });  $mediaIcon = this.css({    marginBottom: "0.5rem",    width: "2.5rem",    height: "2.5rem",    borderRadius: "0.5rem",    backgroundColor: t.muted,    color: t.mutedForeground,  }).on("& svg", { width: "1.25rem", height: "1.25rem" });  $title = this.css({ fontSize: "1.125rem", fontWeight: 500, letterSpacing: "-0.01em" });  $description = this.css({ fontSize: "0.875rem", color: t.mutedForeground }).on("& > a", {    textDecoration: "underline",    textUnderlineOffset: "4px",  });  $content = this.css({    display: "flex",    width: "100%",    maxWidth: "24rem",    flexDirection: "column",    alignItems: "center",    gap: "1rem",    fontSize: "0.875rem",  });}export interface EmptySlotProps {  class?: string;  id?: string;  children?: Children;}/** Purely presentational empty/blank-state primitives — no Morphos equivalent, same as upstream shadcn/ui. */@Component()export class Empty extends StatelessComponent<EmptySlotProps> {  @Styled(EmptyStyles) $s!: EmptyStyles;  render() {    const { class: cls, id, children } = this.props;    return (      <div id={id} class={cx(this.$s.$root, cls)}>        {children}      </div>    );  }}@Component()export class EmptyHeader extends StatelessComponent<EmptySlotProps> {  @Styled(EmptyStyles) $s!: EmptyStyles;  render() {    const { class: cls, children } = this.props;    return <div class={cx(this.$s.$header, cls)}>{children}</div>;  }}@Component()export class EmptyMedia extends StatelessComponent<EmptySlotProps & { variant?: "default" | "icon" }> {  @Styled(EmptyStyles) $s!: EmptyStyles;  render() {    const { class: cls, variant = "default", children } = this.props;    return <div class={cx(this.$s.$media, variant === "icon" && this.$s.$mediaIcon, cls)}>{children}</div>;  }}@Component()export class EmptyTitle extends StatelessComponent<EmptySlotProps> {  @Styled(EmptyStyles) $s!: EmptyStyles;  render() {    const { class: cls, children } = this.props;    return <div class={cx(this.$s.$title, cls)}>{children}</div>;  }}@Component()export class EmptyDescription extends StatelessComponent<EmptySlotProps> {  @Styled(EmptyStyles) $s!: EmptyStyles;  render() {    const { class: cls, children } = this.props;    return <div class={cx(this.$s.$description, cls)}>{children}</div>;  }}@Component()export class EmptyContent extends StatelessComponent<EmptySlotProps> {  @Styled(EmptyStyles) $s!: EmptyStyles;  render() {    const { class: cls, children } = this.props;    return <div class={cx(this.$s.$content, cls)}>{children}</div>;  }}

Examples

Usage

import { Button } from "@/components/ui/button";
import {
  Empty,
  EmptyContent,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
} from "@/components/ui/empty";

<Empty>
  <EmptyHeader>
    <EmptyMedia variant="icon">📭</EmptyMedia>
    <EmptyTitle>No messages yet</EmptyTitle>
    <EmptyDescription>When you receive a message, it'll show up here.</EmptyDescription>
  </EmptyHeader>
  <EmptyContent>
    <Button>Compose a message</Button>
  </EmptyContent>
</Empty>

Props

EmptyMedia

PropTypeDefault
variant"default" | "icon""default"

Empty, EmptyHeader, EmptyTitle, EmptyDescription, and EmptyContent take no props beyond class/children.

On this page