WUWensity UI

Label

Form labels with required and optional markers.

Installation

npx shadcn@latest add https://layro.dev/r/wensity/label.json

Or, with @layro in your components.json:

npx shadcn@latest add @layro/wensity/label

Code

"use client";

import * as React from "react";
import { cn } from "@/lib/utils";

export type LabelVariant =
  | "default"
  | "required"
  | "optional"
  | "muted"
  | "inline";

export interface LabelProps
  extends React.LabelHTMLAttributes<HTMLLabelElement> {
  variant?: LabelVariant;
  /** Secondary copy shown beside the label text — most useful with `inline`. */
  hint?: React.ReactNode;
  disabled?: boolean;
}

const base = "text-[13px] leading-none tracking-[-0.01em]";

const variantClasses: Record<LabelVariant, string> = {
  default: cn(base, "mb-2 block font-medium text-[var(--foreground)]"),
  required: cn(base, "mb-2 block font-medium text-[var(--foreground)]"),
  optional: cn(base, "mb-2 block font-medium text-[var(--foreground)]"),
  muted: cn(
    base,
    "block text-[12px] font-normal leading-[1.4] text-[var(--primitive-text-secondary)]",
  ),
  inline: cn(
    base,
    "inline-flex min-w-[7.5rem] items-center gap-2 font-medium text-[var(--foreground)]",
  ),
};

function RequiredMarker() {
  return (
    <span
      aria-hidden="true"
      className="ml-1 font-medium text-[var(--primitive-destructive)]"
    >
      *
    </span>
  );
}

function OptionalMarker() {
  return (
    <span
      role="note"
      className="ml-2 text-[length:var(--primitive-text-hint)] font-medium text-[var(--primitive-text-secondary)]"
    >
      Optional
    </span>
  );
}

export const labelVariants = variantClasses;

export function Label({
  variant = "default",
  hint,
  disabled = false,
  className,
  children,
  ...props
}: LabelProps) {
  const showRequired = variant === "required";
  const showOptional = variant === "optional";
  const isInline = variant === "inline";

  return (
    <label data-wensity-primitive=""
      data-slot="label"
      data-variant={variant}
      data-disabled={disabled ? "" : undefined}
      className={cn(
        variantClasses[variant],
        disabled && "cursor-not-allowed opacity-45",
        className,
      )}
      {...props}
    >
      <span className={cn(isInline && "inline-flex items-center gap-2")}>
        <span>{children}</span>
        {showRequired ? <RequiredMarker /> : null}
        {showOptional ? <OptionalMarker /> : null}
        {hint ? (
          <span
            className={cn(
              "font-normal text-[var(--primitive-text-secondary)]",
              isInline ? "text-[12px]" : "mt-1 block text-[12px] leading-[1.45]",
            )}
          >
            {hint}
          </span>
        ) : null}
      </span>
    </label>
  );
}

Versions

  1. 1.0.0 · current

    Oct 27, 2026

    First release.

Similar components