Overview
To better understard how BaseLayer works, lets take a look at some examples.
Base
Slots
Let’s dive with an example. Take a look at this ComboBox component.
The Base Layer for the component looks like this.
import type {
ComboBoxProps as AriaComboBoxProps,
ItemProps,
} from "react-aria-components";
import {
ComboBox as AriaComboBox,
Button,
Input,
Item,
Label,
ListBox,
Popover,
Text,
} from "react-aria-components";
import { ChevronDown } from "lucide-react";
import { tv } from "tailwind-variants";
const combobox = tv({
slots: {
input: "m-0 w-64 rounded-md border bg-surface p-2 align-middle text-fg",
root: "max-h-inherit overflow-auto p-1 outline-none",
item: "relative m-1 flex cursor-default flex-col rounded-md p-2 outline-none hover:bg-surface-2 aria-selected:bg-secondary aria-selected:text-secondary-fg",
popover:
"w-64 rounded-xl border bg-surface p-2 text-fg shadow-xl outline-none",
button:
"absolute right-2 flex appearance-none items-center justify-center rounded-md border-0 outline-none hover:bg-surface-2",
},
});
const { input, button, item, popover, root } = combobox();
interface ComboBoxProps<T extends object>
extends Omit<AriaComboBoxProps<T>, "children"> {
className?: string;
label?: string;
description?: string | null;
errorMessage?: string | null;
children: React.ReactNode | ((item: T) => React.ReactNode);
}
const ComboBox = <T extends object>({
label,
className,
description,
errorMessage,
children,
...props
}: ComboBoxProps<T>) => (
<AriaComboBox className={root({ className })} {...props}>
<Label className="text-fg">{label}</Label>
<div className="relative flex w-fit items-center rounded-2xl bg-surface">
<Input className={input()} />
<Button className={button()}>
<ChevronDown className="text-fg"/>
</Button>
</div>
{description && <Text slot="description">{description}</Text>}
{errorMessage && <Text slot="errorMessage">{errorMessage}</Text>}
<Popover className={popover()}>
<ListBox>{children}</ListBox>
</Popover>
</AriaComboBox>
);
ComboBox.displayName = "ComboBox";
const ComboBoxItem = (props: ItemProps) => (
<Item {...props} className={item()} />
);
ComboBoxItem.displayName = "ComboBoxItem";
export { ComboBox, ComboBoxItem };
and the using the component itself like this.
import { ComboBox, ComboBoxItem } from "@/components/base/combobox";
export const ComboBoxExample = () => {
return (
<ComboBox label="Project">
<ComboBoxItem>Health Dashboard</ComboBoxItem>
<ComboBoxItem>To-Do App</ComboBoxItem>
<ComboBoxItem>UI Kit</ComboBoxItem>
<ComboBoxItem>Portfolio Site</ComboBoxItem>
</ComboBox>
);
};
As you can see, BaseLayer leverages the Tailwind Variants library to create reusable styles. The TV library provides usefull slots, which are perfect for handling the styles of complex multi part Ark Components.
Variants
Tailwind Variants (as the name implies) also provies support for creating style variants as well as compound and even responsive variants.
Let’s take a look at how variants work in BaseLayer.
Here is a Button component using the button with several variants.
import { button } from "@/components/base/button";
<Button intent="secondary" state="outline">Button</Button>;
You can use the variants inline like shown above, or pass them as props to your component itself.
Overrides
Tailwind Variants provides excellent support for overriding styles and handling conflicts. The library uses tw-merge under the hood. All Tailwind Variants components provide an optional class / className for overriding classes on any component. Be sure to check out the Tailwind Variants docs for more info info.
Examples
Assembled components built from the Base components, such as the ComboBox above are distributed via VScode snippets. This allows you to rapidly build complex systems with just a few keystrokes. Have a look at the VsCode Docs for how to use.