@otto201613/yoo-ui
v0.0.2
Published
React UI components for Yoo applications.
Maintainers
Readme
@otto201613/yoo-ui
Reusable React 19 components from the Yoo design system. The package includes typed ESM and
CommonJS builds, TypeScript declarations, the UI theme, and its runtime dependency on
@otto201613/yoo-icons.
Installation
pnpm add @otto201613/yoo-ui @otto201613/yoo-icons react react-dom
pnpm add -D tailwindcss @tailwindcss/postcssEquivalent npm install and yarn add commands also work. The package requires Node.js 18 or newer,
React 19, and Tailwind CSS 4.
Tailwind CSS setup
The components are built from Tailwind CSS 4 utility classes. Import Tailwind and the exported theme, then add the published bundle as an explicit Tailwind source in your application's global CSS:
@import 'tailwindcss';
@import '@otto201613/yoo-ui/styles';
@source '../node_modules/@otto201613/yoo-ui/dist';The @source path is relative to the CSS file. For example, use
../../node_modules/@otto201613/yoo-ui/dist when the stylesheet is src/app/globals.css.
For a PostCSS-based app, enable the Tailwind CSS 4 plugin:
// postcss.config.mjs
export default {
plugins: {
'@tailwindcss/postcss': {},
},
}The style entry provides the dark Yoo color tokens, button dimensions and radii used by the components. Import it once at the application root. Components that use state, portals, or motion are client components and can be rendered normally from a Next.js App Router application.
Package exports
| Entry | Contents |
| --------------------------- | ------------------------------------------------- |
| @otto201613/yoo-ui | All React components and public TypeScript types |
| @otto201613/yoo-ui/styles | Tailwind theme tokens and component CSS variables |
Components
| Group | Exports | Purpose |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| Actions | YooButton, YooLinkButton, YooIconButton, YooIconButtonGroup | Primary, secondary, text, and icon actions |
| Forms | YooInputGroup, YooInputNumberGroup, YooCheckBox, YooCheckBoxSqure, YooDropdown, YooSelectRow, YooSwitchButton, YooTextFiledSuffix | Inputs, choices, dropdowns, and toggles |
| Navigation | YooTabs | Controlled or uncontrolled tabs with line, card, button, and text variants |
| Data | YooTable, YooListCard, YooListTable | Key/value layouts, data tables, and responsive list presentations |
| Overlays | YooModal, YooPopover | Layered dialogs and click/hover popovers |
| Feedback | YooEmpty, YooLoader, YooNoMore, YooTipsWarn | Empty, loading, end-of-list, and warning states |
| Display | YooAvatar, YooUserAvatar, YooCounter, YooGlowDot, YooTitle, YooToggle | Avatars, counters, status, headings, and collapsible content |
YooCheckBoxSqure and YooTextFiledSuffix use their current exported spellings for compatibility.
Basic usage
The following example combines a button, a V2 icon, and a loading state:
'use client'
import { YooButton } from '@otto201613/yoo-ui'
import { RightArrowIcon } from '@otto201613/yoo-icons/V2'
export function ContinueButton({ pending = false }: { pending?: boolean }) {
return (
<YooButton
color="main"
variant="solid"
size="lg"
pending={pending}
pendingChildren="Submitting..."
suffixNode={<RightArrowIcon className="h-6 w-6" aria-hidden="true" />}
>
Continue
</YooButton>
)
}YooButton supports:
- colors:
main,gray,cyan,brand,red - variants:
solid,outlined - sizes:
xl,lg,md,sm,xs - native button props plus
pending,pendingChildren,prefixNode, andsuffixNode
Inputs and selection
'use client'
import { useState } from 'react'
import { YooDropdown, YooInputGroup, YooSelectRow, YooSwitchButton } from '@otto201613/yoo-ui'
import { EmailOutlinedIcon, RightArrowIcon } from '@otto201613/yoo-icons/V2'
export function AccountForm() {
const [email, setEmail] = useState('')
const [currency, setCurrency] = useState('usd')
const [enabled, setEnabled] = useState(true)
return (
<div className="flex max-w-120 flex-col gap-4">
<YooInputGroup
label="Email"
size="lg"
prefix={<EmailOutlinedIcon className="h-6 w-6" aria-hidden="true" />}
placeholder="[email protected]"
isClear={email.length > 0}
onClear={() => setEmail('')}
field={{
value: email,
onChange: (event) => setEmail(event.target.value),
}}
/>
<YooDropdown
value={currency}
options={[
{ value: 'usd', label: 'USD' },
{ value: 'eur', label: 'EUR' },
]}
onSelect={setCurrency}
/>
<YooSelectRow
variant="option"
selected={currency === 'usd'}
description="Set USD as the settlement currency"
suffixNode={<RightArrowIcon className="h-6 w-6" aria-hidden="true" />}
onClick={() => setCurrency('usd')}
>
US Dollar
</YooSelectRow>
<YooSwitchButton checked={enabled} onCheckedChange={setEnabled} />
</div>
)
}YooSelectRow variants are field, option, account, and dashed. YooInputNumberGroup accepts
the same input-group shape as YooInputGroup and configures a numeric input.
Tabs
YooTabs supports controlled and uncontrolled state. Each item may provide its own content:
'use client'
import { useState } from 'react'
import { YooTabs, type TabItemType } from '@otto201613/yoo-ui'
const items: TabItemType[] = [
{ key: 'open', label: 'Open', content: <div>Open orders</div> },
{ key: 'history', label: 'History', content: <div>Order history</div> },
{ key: 'disabled', label: 'Unavailable', disabled: true },
]
export function OrdersTabs() {
const [activeKey, setActiveKey] = useState('open')
return (
<YooTabs
items={items}
activeKey={activeKey}
onChange={(_, key) => setActiveKey(key)}
variant="card"
color="brand"
ariaLabel="Orders"
tabIdPrefix="orders-tab"
panelIdPrefix="orders-panel"
/>
)
}Available variants are line, card, button, and text; colors are main and brand.
Modal and popover
'use client'
import { useState } from 'react'
import { YooButton, YooModal, YooPopover } from '@otto201613/yoo-ui'
export function OverlayExample() {
const [show, setShow] = useState(false)
return (
<div className="flex gap-4">
<YooButton color="brand" variant="solid" size="md" onClick={() => setShow(true)}>
Open modal
</YooButton>
<YooPopover content="Additional information" placement="bottom" trigger="click" arrow>
<button type="button">Details</button>
</YooPopover>
<YooModal
show={show}
onClose={() => setShow(false)}
title="Confirm action"
placement="center"
triggerClose="all"
>
<p className="text-icon-02">This action can be reviewed before submission.</p>
</YooModal>
</div>
)
}For nested modals, use layer="next" and then layer="elevated" instead of adding arbitrary
z-index values. triggerClose accepts all, modal, or button. If the application has an element
with id="yoo-app-shell", the modal and dropdown components use it as their scroll/layout boundary.
Tables
YooTable has a simple key/value mode and a typed data-table mode:
import { YooTable, type TableColumn } from '@otto201613/yoo-ui'
type Order = {
id: string
status: string
total: number
}
const columns: TableColumn<Order>[] = [
{ key: 'id', title: 'Order', dataIndex: 'id' },
{ key: 'status', title: 'Status', dataIndex: 'status' },
{
key: 'total',
title: 'Total',
align: 'right',
render: (_, order) => `$${order.total.toFixed(2)}`,
},
]
export function OrdersTable({ orders }: { orders: Order[] }) {
return (
<YooTable<Order> type="data" columns={columns} dataSource={orders} emptyText="No orders" />
)
}Key/value mode:
<YooTable
rows={[
{ key: 'network', label: 'Network', value: 'Ethereum' },
{ key: 'status', label: 'Status', value: 'Confirmed' },
]}
headerLabel="Field"
headerValue="Value"
/>Data-table rows can also be expandable through the expandable prop.
Avatars and empty states
import { YooButton, YooEmpty, YooUserAvatar } from '@otto201613/yoo-ui'
export function ProfileSummary() {
return (
<div className="flex flex-col items-center gap-6">
<YooUserAvatar src="/avatar.png" alt="Otto" level={18} size={3} color="#5a3bb8" />
<YooEmpty
type="list"
text="No items yet"
button={
<YooButton color="main" variant="solid" size="sm">
Browse items
</YooButton>
}
/>
</div>
)
}Public TypeScript types
The root entry exports these named types:
- buttons:
YooButtonColor,YooButtonProps,YooButtonSize,YooButtonVariant,YooLinkButtonProps - tabs:
TabItemType,YooTabLabelType,YooTabsColor,YooTabsType,YooTabsVariant - selection:
YooSelectRowProps,YooSelectRowVariant - overlays:
ModalTypes,YooModalLayer,YooPopoverProps,PopoverPlacement - tables:
KeyValueRowData,TableColumn,KeyValueTableProps,DataTableProps,YooTableProps,ExpandableConfig - avatars:
YooUserAvatarProps,UserAvatarSize
Use import type { ... } from '@otto201613/yoo-ui' so type-only imports are removed from runtime
bundles.
Notes
- Import
@otto201613/yoo-ui/stylesonly once. - Keep the
@sourcerule so Tailwind generates the utilities used inside the published bundle. - The npm package is for external consumers. Contributors inside the YooBox monorepo continue to use
the local
@gjmh/uiand@gjmh/iconsworkspace package names. - The package does not include React or React DOM; they are peer dependencies supplied by the app.
