smart_button

Button

Primary action trigger with variants and states.

Button Examples

M3-ish button with variants, sizes, optional icon, ripple effect, loading state, and optional fixed positioning.

Basic usage

Use it as a regular button. Add `onClick` for interaction.

Live preview

Basic

import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  return (
    <Button variant="primary" ripple onClick={() => console.log("clicked")}>
      Click
    </Button>
  );
}

Live preview

Click counter (controlled state)

import { useState } from "react";
import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  const [count, setCount] = useState(0);

  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: 12, alignItems: "center" }}>
      <Button variant="primary" ripple onClick={() => setCount((v) => v + 1)}>
        Clicked: {count}
      </Button>

      <Button variant="transparent" outline onClick={() => setCount(0)}>
        Reset
      </Button>
    </div>
  );
}

Variants

Change the visual tone with `variant`.

Live preview

All variants

import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: 12 }}>
      <Button variant="primary">Primary</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="tertiary">Tertiary</Button>
      <Button variant="success">Success</Button>
      <Button variant="warning">Warning</Button>
      <Button variant="danger">Danger</Button>
      <Button variant="info">Info</Button>
      <Button variant="transparent" outline>Transparent</Button>
    </div>
  );
}

Live preview

Outline + ripple

import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: 12 }}>
      <Button variant="primary" outline ripple>Primary</Button>
      <Button variant="secondary" outline ripple>Secondary</Button>
      <Button variant="danger" outline ripple>Danger</Button>
    </div>
  );
}

Sizes and shapes

Use `size` and `variantRadio` to control scale and radius/shape.

Live preview

Sizes

import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  return (
    <div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
      <Button size="small" variant="primary">Small</Button>
      <Button size="medium" variant="primary">Medium</Button>
      <Button size="large" variant="primary">Large</Button>
    </div>
  );
}

Live preview

Radius levels + aliases

import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  return (
    <div style={{ display: "grid", gap: 12 }}>
      <div style={{ display: "flex", flexWrap: "wrap", gap: 12 }}>
        <Button variant="secondary" variantRadio="none">none</Button>
        <Button variant="secondary" variantRadio="xs">xs</Button>
        <Button variant="secondary" variantRadio="sm">sm</Button>
        <Button variant="secondary" variantRadio="md">md</Button>
        <Button variant="secondary" variantRadio="lg">lg</Button>
        <Button variant="secondary" variantRadio="xl">xl</Button>
        <Button variant="secondary" variantRadio="full">full</Button>
      </div>

      <div style={{ display: "flex", flexWrap: "wrap", gap: 12 }}>
        <Button variant="tertiary" variantRadio="square">square</Button>
        <Button variant="tertiary" variantRadio="rounded">rounded</Button>
        <Button variant="tertiary" variantRadio="pill">pill</Button>
      </div>
    </div>
  );
}

Icons

Use `iconContent` to render an icon next to the label. Combine with `iconPosition` and `iconSize`.

Live preview

Icon component

import Icon from "@/packages/ui/src/_components/icon/Icon";
import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: 12 }}>
      <Button
        variant="info"
        ripple
        iconPosition="left"
        iconSize="small"
        iconContent={<Icon name="home" size="small" />}
      >
        Home
      </Button>

      <Button
        variant="warning"
        outline
        iconPosition="right"
        iconSize="medium"
        iconContent={<Icon name="east" style="rounded" />}
      >
        Next
      </Button>

      <Button
        variant="success"
        iconPosition="left"
        iconSize="medium"
        iconContent={<Icon name="check" />}
      >
        Done
      </Button>
    </div>
  );
}

Live preview

String icons

import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  return (
    <div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
      <Button iconContent="⭐">Star</Button>
      <Button iconContent="🔥">Hot</Button>
    </div>
  );
}

Live preview

Icon-only button (accessibility)

import Icon from "@/packages/ui/src/_components/icon/Icon";
import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  return (
    <div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
      <Button
        variant="transparent"
        outline
        ripple
        ariaLabel="Search"
        iconContent={<Icon name="search" />}
      />
      <Button
        variant="transparent"
        outline
        ripple
        ariaLabel="Settings"
        iconContent={<Icon name="settings" />}
      />
    </div>
  );
}

States and layout

Loading, disabled, full width, and forced dimensions.

Live preview

Loading (and disable other actions)

import { useState } from "react";
import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  const [loading, setLoading] = useState(false);

  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: 12 }}>
      <Button
        variant="secondary"
        ripple
        loading={loading}
        onClick={() => setLoading(true)}
      >
        Save
      </Button>

      <Button
        variant="danger"
        outline
        ripple
        disabled={loading}
        onClick={() => setLoading(false)}
      >
        Cancel
      </Button>
    </div>
  );
}

Live preview

Disabled

import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  return (
    <div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
      <Button variant="primary" disabled>Disabled</Button>
      <Button variant="secondary" outline disabled>Disabled outline</Button>
    </div>
  );
}

Live preview

Full width + forced sizes

import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  return (
    <div style={{ display: "grid", gap: 12, width: 320 }}>
      <Button variant="primary" fullWidth>Full width</Button>
      <Button variant="info" forceWidth="200px" forceHeight="44px">200px × 44px</Button>
    </div>
  );
}

Live preview

Custom styles and extra classes

import { Button } from "@/packages/ui/src/_components/buttons_variants/buttons/Button";

export default function Example() {
  return (
    <div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
      <Button
        variant="tertiary"
        className="my-extra-class"
        customStyles={{ backgroundColor: "pink", padding: "12px 20px" }}
      >
        Custom styles
      </Button>

      <Button defaultButton>Default button</Button>
    </div>
  );
}

Fixed positioning (FAB-style)

Use `isFixed` and `fixedPosition` for floating action buttons. For docs, only code is shown to avoid interfering with the page layout.

Tip: for icon-only FABs, always set `ariaLabel`.

Combine with `variantRadio='full'` for a circular shape.

Four corners

import Icon from "@/_components/icon/Icon";
import { Button } from "@/_components/buttons_variants/buttons/Button";

export default function Example() {
  return (
    <>
      <Button
        variant="primary"
        variantRadio="full"
        size="large"
        isFixed
        fixedPosition="up-left"
        ariaLabel="Create"
        iconContent={<Icon name="add" size="large" style="rounded" />}
        ripple
      />

      <Button
        variant="secondary"
        variantRadio="full"
        size="large"
        isFixed
        fixedPosition="up-right"
        ariaLabel="Search"
        iconContent={<Icon name="search" size="large" style="rounded" />}
        ripple
      />

      <Button
        variant="tertiary"
        variantRadio="xl"
        size="large"
        isFixed
        fixedPosition="down-left"
        iconPosition="left"
        iconContent={<Icon name="edit" size="medium" style="rounded" />}
        ripple
      >
        Edit
      </Button>

      <Button
        variant="success"
        variantRadio="xl"
        size="large"
        isFixed
        fixedPosition="down-right"
        iconPosition="left"
        iconContent={<Icon name="chat" size="medium" style="rounded" />}
        ripple
      >
        New
      </Button>
    </>
  );
}

On this page

Button Props

Button — Props
PropTypeRequiredDefaultDescription
size"small" | "medium" | "large"NoundefinedVisual size preset.
variant"primary" | "secondary" | "tertiary" | "success" | "warning" | "danger" | "info" | "transparent"NoundefinedVisual tone/variant.
variantRadio"none" | "xs" | "sm" | "md" | "lg" | "xl" | "full" | "square" | "rounded" | "pill"NoundefinedRadius/shape preset (maps to `cssnt-button--radius-*` / `cssnt-button--shape-*`).
outlinebooleanNofalseEnables outline styling class.
ripplebooleanNofalseEnables ripple effect on pointer down (disabled while loading or disabled).
loadingbooleanNofalseWhen true, renders a spinner and hides label/icon.
disabledbooleanNofalseAdds a disabled styling class and disables ripple.
fullWidthbooleanNofalseAdds a full-width styling class.
iconContentReact.ReactNode | stringNoundefinedOptional icon rendered next to the label (or as icon-only when no children).
iconPosition"left" | "right"NoundefinedIcon placement (CSS class).
iconSize"small" | "medium" | "large"NoundefinedIcon wrapper size class.
forceHeightstringNoundefinedForces button min/max height (e.g. "40px").
forceWidthstringNoundefinedForces button min/max width (e.g. "160px").
isFixedbooleanNofalseEnables fixed-position mode (adds `cssnt-button--is-fixed`).
fixedPosition"up-right" | "down-right" | "up-left" | "down-left"NoundefinedFixed-position placement preset.
defaultButtonbooleanNofalseEnables a reset/default style (useful for drop-in compatibility).
ariaLabelstringNoundefinedAccessibility label (recommended for icon-only buttons).
customStylesReact.CSSPropertiesNoundefinedInline styles applied to the root element.
classNamestringNoundefinedExtra classes appended to the root element.
childrenReact.ReactNodeNoundefinedButton label/content.
onClick(event: React.MouseEvent<HTMLButtonElement>) => voidNoundefinedClick handler.