@uptodateconnect/ui
v0.4.0
Published
UP TO DATE shared React + Tailwind components
Readme
@uptodateconnect/ui
Shared React + Tailwind components that give every UP TO DATE product the same look for AI features: one icon, one color, one set of animations.
- React 18+, TypeScript, Tailwind CSS 3.4+ or 4
- Light and dark mode
- Import everything from the root, or one component at a time
- Respects
prefers-reduced-motion
Install
npm install @uptodateconnect/uiPeer dependencies: react, react-dom, tailwindcss.
Tailwind setup (required)
Tailwind has to scan the package, otherwise the layout classes are not generated.
Tailwind v4, in your main CSS file:
@source "../node_modules/@uptodateconnect/ui/dist";Tailwind v3, in tailwind.config.js:
content: ["./src/**/*.{ts,tsx}", "./node_modules/@uptodateconnect/ui/dist/**/*.js"]Usage
import { AiButton, AiInput, AiBadge } from "@uptodateconnect/ui";
// or one component at a time
import { AiButton } from "@uptodateconnect/ui/ai-button";
export function Example() {
return (
<>
<AiBadge>AI-generated</AiBadge>
<AiInput placeholder="Ask AI anything..." />
<AiButton shine>Generate with AI</AiButton>
</>
);
}Components
Animations are opt-in (off by default) unless noted. Every component accepts the normal props of its HTML element (className, onClick, ...).
AI
| Component | Import path | What it is |
|---|---|---|
| AiIcon | /ai-icon | The official AI sparkle icon. Use it instead of wands or other sparkles. |
| AiSpinner | /ai-spinner | The official loading spinner (twinkling sparkles). Use it for every loading state. |
| AiButton | /ai-button | Gradient button with the AI icon. |
| AiBadge | /ai-badge | Small pill that marks content as AI-generated. |
| AiInput | /ai-input | Text input with a gradient border and the AI icon. |
| AiContainer | /ai-container | Wrapper for AI content, with a rotating border. |
| AiImageLoader | /ai-image-loader | Preloader while an AI image is generated. |
Chat
| Component | Import path | What it is |
|---|---|---|
| AiMessage | /ai-message | One chat bubble, for the assistant or the user. |
| AiChatInput | /ai-chat-input | Message composer. Enter sends, Shift+Enter adds a new line. |
| AiSuggestions | /ai-suggestions | Clickable prompt suggestion chips. |
| AiThinking | /ai-thinking | "AI is thinking" indicator. |
Import paths are shown relative to @uptodateconnect/ui, for example @uptodateconnect/ui/ai-button.
Props
AiButton
| Prop | Type | Default | Notes |
|---|---|---|---|
| variant | "primary" \| "secondary" \| "outline" \| "ghost" | "primary" | Primary is solid brand blue. Secondary is a light blue tint. Outline is white with a gray border. Ghost has no border. ("soft" is the old name for secondary and still works.) |
| size | "sm" \| "md" \| "lg" | "md" | |
| iconOnly | boolean | false | Square button with only the icon. Add an aria-label. |
| icon | ReactNode | AI sparkle | Replaces the sparkle. null removes it. |
| loading | boolean | false | Shows the AiSpinner instead of the icon and disables the button. |
| shine | boolean | false | Shine across the caption, once on mount and on hover. |
| animatedBorder | boolean | false | Rotating gradient border. Adds 2px per side. |
AiSpinner
| Prop | Type | Default | Notes |
|---|---|---|---|
| size | number | 20 | Width and height in px. |
| label | string | "Loading" | Accessible label. Use "" when the parent already says it is loading. |
It takes the current text color, and stays still when the user has reduced motion on.
AiBadge
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | "AI" | Badge text. |
| tone | "brand" \| "success" \| "purple" \| "neutral" \| "warning" \| "info" | "brand" | Color of the badge. |
| icon | ReactNode | AI sparkle | Replaces the sparkle. null removes it. |
| shine | boolean | false | Shine across the text. |
AiInput
| Prop | Type | Default | Notes |
|---|---|---|---|
| shine | boolean | false | Shine across the placeholder while the input is empty. Needs a placeholder. |
AiContainer
| Prop | Type | Default | Notes |
|---|---|---|---|
| animatedBorder | boolean | false | Rotating gradient border. |
| sparkles | boolean | true | Faint decorative sparkles in the corner. |
| shine | boolean | false | Shines the text inside (p, headings, li, blockquote, label, figcaption, or anything with data-ai-shine). Elements with their own background are left alone. |
AiImageLoader
| Prop | Type | Default | Notes |
|---|---|---|---|
| aspectRatio | string | "1 / 1" | CSS aspect ratio. Match the final image to avoid layout jumps. |
| label | string | none | Text in the center. |
| showIcon | boolean | false | AI icon in the center. |
| progress | number (0-100) | none | Shows a thin progress bar when set. |
Fills the width of its parent. Size it with className (for example max-w-sm). Loops continuously.
AiMessage
| Prop | Type | Default | Notes |
|---|---|---|---|
| role | "assistant" \| "user" | "assistant" | Assistant is left with an AI avatar. User is right in the brand gradient. |
Plain text only. It does not render markdown.
AiChatInput
| Prop | Type | Default | Notes |
|---|---|---|---|
| onSend | (text: string) => void | none | Called with the trimmed text. |
| placeholder | string | "Ask anything..." | |
| onAttach | () => void | none | Shows a paperclip button. Open your own file picker in the handler. |
| loading | boolean | false | Blocks sending while waiting for the AI. |
| disabled | boolean | false | |
It manages its own text. It grows as the user types, up to a limit.
AiSuggestions
| Prop | Type | Notes |
|---|---|---|
| suggestions | (string \| { label: string; icon?: ReactNode })[] | Chip labels. Use the object form for your own icon. The default icon is the AI sparkle. |
| onSelect | (suggestion: string) => void | Called with the chosen text. |
AiThinking
| Prop | Type | Default | Notes |
|---|---|---|---|
| variant | "bubble" \| "ghost" | "bubble" | Bubble is a tinted chat bubble. Ghost has no box: just the sparkle and text. |
| label | string | "Thinking" | |
| dots | boolean | true | Bouncing dots. Loops continuously. |
| shine | boolean | true | Looping shine on the label. |
Chat example
<AiMessage role="user">Summarize my last report.</AiMessage>
<AiMessage>Sure, here is the summary...</AiMessage>
{loading && <AiThinking />}
<AiSuggestions suggestions={["Draft a reply"]} onSelect={send} />
<AiChatInput onSend={send} loading={loading} />Brand colors
All colors live in one place: src/lib/ai-brand.ts. Edit it and publish a new version to rebrand every AI component in every project.
A single project can also override the colors with CSS variables, without touching the package:
:root {
--utd-ai-from: #0b92fa; /* primary */
--utd-ai-to: #0181ef; /* gradient end */
--utd-ai-soft: #e7f4fe; /* light tint (badges, bubbles) */
--utd-ai-border: #a5d3fb;
--utd-ai-text: #0567c8;
}Dark-mode tints use --utd-ai-soft-dark, --utd-ai-border-dark and --utd-ai-text-dark.
Dark mode
Components use Tailwind dark: classes, so they follow your project's own dark-mode setup:
- Tailwind v4: follows the OS (
prefers-color-scheme) by default. For a class toggle, add this to your main CSS:@custom-variant dark (&:where(.dark, .dark *)); - Tailwind v3: set
darkMode: "class", or leave the defaultmedia.
Development
npm install
npm run storybook # preview components at http://localhost:6006
npm run typecheck
npm run build # builds dist/Storybook has a Theme switcher in the toolbar to check light and dark.
Add a component
- Create
src/components/<name>.tsxand<name>.stories.tsx. - Export it from
src/index.ts. - Add an entry in
tsup.config.tsand anexportssubpath inpackage.json.
Publish
npm login
npm version patch # or minor / major
npm publish # runs typecheck + build firstThe package is public under the uptodateconnect npm organization. npm does not accept the same version twice, so bump the version for every release.
