Components
Input Group
Pairs an Input with leading/trailing addons. Purely presentational.
Installation
npx kosmesis add input-grouppnpm dlx kosmesis add input-groupyarn dlx kosmesis add input-groupbunx kosmesis add input-groupCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { cn } from "@/lib/utils";export interface InputGroupProps { class?: string; id?: string; children?: Children;}/** Purely presentational — no Morphos equivalent, same as upstream shadcn/ui. Pair with `Input`. */@Component()export class InputGroup extends StatelessComponent<InputGroupProps> { render() { const { class: cls, id, children } = this.props; return ( <div id={id} class={cn( "group/input-group flex h-9 w-full items-center rounded-md border border-input bg-transparent shadow-xs transition-[color,box-shadow]", "has-[[data-focused]]:border-ring has-[[data-focused]]:ring-[3px] has-[[data-focused]]:ring-ring/50", "has-[[data-invalid]]:border-destructive has-[[data-invalid]]:ring-destructive/20", "[&>input]:h-full [&>input]:flex-1 [&>input]:border-0 [&>input]:bg-transparent [&>input]:px-3 [&>input]:shadow-none [&>input]:outline-none [&>input]:focus-visible:ring-0", cls, )} > {children} </div> ); }}export interface InputGroupAddonProps { align?: "start" | "end"; class?: string; children?: Children;}@Component()export class InputGroupAddon extends StatelessComponent<InputGroupAddonProps> { render() { const { align = "start", class: cls, children } = this.props; return ( <div data-align={align} class={cn( "flex items-center gap-2 px-3 text-muted-foreground [&_svg]:size-4", align === "start" && "order-first", align === "end" && "order-last", cls, )} > {children} </div> ); }}export interface InputGroupTextProps { class?: string; children?: Children;}@Component()export class InputGroupText extends StatelessComponent<InputGroupTextProps> { render() { const { class: cls, children } = this.props; return <span class={cn("text-sm text-muted-foreground", cls)}>{children}</span>; }}Install the following dependencies:
npm install @praxisjs/csspnpm add @praxisjs/cssyarn add @praxisjs/cssbun add @praxisjs/cssCopy and paste the following code into your project.
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 InputGroupStyles extends Stylesheet { $root = this.css({ display: "flex", height: "2.25rem", width: "100%", alignItems: "center", borderRadius: `calc(${t.radius} - 2px)`, border: `1px solid ${t.input}`, backgroundColor: "transparent", boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", transition: "color 120ms ease, box-shadow 120ms ease", }) .has("[data-focused]", { borderColor: t.ring, boxShadow: `0 0 0 3px color-mix(in oklab, ${t.ring} 50%, transparent)` }) .has("[data-invalid]", { borderColor: t.destructive, boxShadow: `0 0 0 3px color-mix(in oklab, ${t.destructive} 20%, transparent)` }) .on("& > input", { height: "100%", flex: "1 1 0%", border: "0", backgroundColor: "transparent", padding: "0 0.75rem", boxShadow: "none", outline: "none", }); $addon = this.css({ display: "flex", alignItems: "center", gap: "0.5rem", padding: "0 0.75rem", color: t.mutedForeground }) .on("& svg", { width: "1rem", height: "1rem" }) .on('&[data-align="start"]', { order: "-1" }) .on('&[data-align="end"]', { order: "1" }); $text = this.css({ fontSize: "0.875rem", color: t.mutedForeground });}export interface InputGroupProps { class?: string; id?: string; children?: Children;}/** Purely presentational — no Morphos equivalent, same as upstream shadcn/ui. Pair with `Input`. */@Component()export class InputGroup extends StatelessComponent<InputGroupProps> { @Styled(InputGroupStyles) $s!: InputGroupStyles; render() { const { class: cls, id, children } = this.props; return ( <div id={id} class={cx(this.$s.$root, cls)}> {children} </div> ); }}export interface InputGroupAddonProps { align?: "start" | "end"; class?: string; children?: Children;}@Component()export class InputGroupAddon extends StatelessComponent<InputGroupAddonProps> { @Styled(InputGroupStyles) $s!: InputGroupStyles; render() { const { align = "start", class: cls, children } = this.props; return ( <div data-align={align} class={cx(this.$s.$addon, cls)}> {children} </div> ); }}export interface InputGroupTextProps { class?: string; children?: Children;}@Component()export class InputGroupText extends StatelessComponent<InputGroupTextProps> { @Styled(InputGroupStyles) $s!: InputGroupStyles; render() { const { class: cls, children } = this.props; return <span class={cx(this.$s.$text, cls)}>{children}</span>; }}Examples
Usage
import { Input } from "@/components/ui/input";
import { InputGroup, InputGroupAddon, InputGroupText } from "@/components/ui/input-group";
<InputGroup>
<InputGroupAddon>
<InputGroupText>$</InputGroupText>
</InputGroupAddon>
<Input type="number" placeholder="0.00" />
<InputGroupAddon align="end">
<InputGroupText>USD</InputGroupText>
</InputGroupAddon>
</InputGroup>