Navigation

Tabs.

Mono uppercase tabs with an orange underline on the active tab, plus a boxed variant for switching between panels.

Turtle 07 is running quarry.lua on a 16 by 16 area, 32 blocks deep.

Installation#

With the CLI

$npx lanterncn add tabs

This also installs the Lantern theme and any Lantern UI components it depends on. The shadcn CLI works directly too: npx shadcn@latest add httptim/lantern-ui/tabs. See Installation if your project is not set up yet.

Manually

  1. Install the dependencies

    npm install radix-ui class-variance-authority
  2. Copy the source into your project

    components/ui/tabs.tsx
    "use client";
    
    import * as React from "react";
    import { Tabs as TabsPrimitive } from "radix-ui";
    import { cva, type VariantProps } from "class-variance-authority";
    
    import { cn } from "@/lib/utils";
    
    type TabsVariant = "line" | "boxed";
    
    const TabsVariantContext = React.createContext<TabsVariant>("line");
    
    function Tabs({
      className,
      variant = "line",
      ...props
    }: React.ComponentProps<typeof TabsPrimitive.Root> & { variant?: TabsVariant }) {
      return (
        <TabsVariantContext.Provider value={variant}>
          <TabsPrimitive.Root
            data-slot="tabs"
            data-variant={variant}
            className={cn("flex flex-col gap-4 data-[orientation=vertical]:flex-row", className)}
            {...props}
          />
        </TabsVariantContext.Provider>
      );
    }
    
    const tabsListVariants = cva("inline-flex max-w-full items-center overflow-x-auto [scrollbar-width:none]", {
      variants: {
        variant: {
          line: "w-full gap-6 border-b",
          boxed: "w-fit gap-1 rounded-md border bg-card p-1",
        },
      },
      defaultVariants: { variant: "line" },
    });
    
    function TabsList({
      className,
      variant,
      ...props
    }: React.ComponentProps<typeof TabsPrimitive.List> & VariantProps<typeof tabsListVariants>) {
      const contextVariant = React.useContext(TabsVariantContext);
      return (
        <TabsPrimitive.List
          data-slot="tabs-list"
          className={cn(tabsListVariants({ variant: variant ?? contextVariant }), className)}
          {...props}
        />
      );
    }
    
    const tabsTriggerVariants = cva(
      "inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 font-mono text-[11px] font-semibold tracking-[0.16em] whitespace-nowrap uppercase transition-colors outline-none disabled:pointer-events-none disabled:opacity-40 focus-visible:ring-2 focus-visible:ring-ring/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
      {
        variants: {
          variant: {
            line: "relative -mb-px h-10 border-b-2 border-transparent px-0.5 text-muted-foreground hover:text-foreground data-[state=active]:border-primary data-[state=active]:text-foreground",
            boxed:
              "h-8 rounded-sm px-3 text-muted-foreground hover:text-foreground data-[state=active]:bg-secondary data-[state=active]:text-primary data-[state=active]:shadow-[inset_0_0_0_1px_var(--color-input)]",
          },
        },
        defaultVariants: { variant: "line" },
      },
    );
    
    function TabsTrigger({
      className,
      variant,
      ...props
    }: React.ComponentProps<typeof TabsPrimitive.Trigger> & VariantProps<typeof tabsTriggerVariants>) {
      const contextVariant = React.useContext(TabsVariantContext);
      return (
        <TabsPrimitive.Trigger
          data-slot="tabs-trigger"
          className={cn(tabsTriggerVariants({ variant: variant ?? contextVariant }), className)}
          {...props}
        />
      );
    }
    
    function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {
      return (
        <TabsPrimitive.Content
          data-slot="tabs-content"
          className={cn("flex-1 rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-ring/25", className)}
          {...props}
        />
      );
    }
    
    export { Tabs, TabsContent, TabsList, TabsTrigger, tabsListVariants, tabsTriggerVariants };
  3. Update the import paths to match your project

    The source imports cn from @/lib/utils.

Usage#

import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
<Tabs defaultValue="program">
  <TabsList>
    <TabsTrigger value="program">Program</TabsTrigger>
    <TabsTrigger value="log">Log</TabsTrigger>
  </TabsList>
  <TabsContent value="program">...</TabsContent>
  <TabsContent value="log">...</TabsContent>
</Tabs>

Set variant="boxed" on Tabs to get the segmented style. The list and triggers pick it up automatically.

The tab list scrolls sideways when there are more tabs than fit on a phone.

Examples#

Boxed

Hub details
The name and address shown in the directory.