Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/green-ducks-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@radui/ui": patch
---

Fix select and combobox popup behavior by improving portal rendering, restoring macOS-style reopen anchoring for `Select`, and tightening related UI polish in the sandbox and shared component styles.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ const data = {
},
type: "string",
default: "null",
},
{
prop: {
name: "iconOnly",
info_tooltips: "Square dimensions for icon-only items; omit when the item has visible text."
},
type: "boolean",
default: "false",
}
]
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const ToggleGroupExampleBasic = () => {
return <div>
<ToggleGroup.Root>
{items.map((item) => (
<ToggleGroup.Item key={item.value} value={item.value}>
<ToggleGroup.Item key={item.value} value={item.value} iconOnly>
{item.label}
</ToggleGroup.Item>
))}
Expand Down
3 changes: 1 addition & 2 deletions docs/next.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/** @type {import('next').NextConfig} */

const createMDX = require('@next/mdx')
const remarkGfm = require('remark-gfm')

const nextConfig = {
// Configure `pageExtensions` to include markdown and MDX files
Expand Down Expand Up @@ -116,7 +115,7 @@ const nextConfig = {
const withMDX = createMDX({
// Add markdown plugins here, as desired
options: {
remarkPlugins: [remarkGfm.default || remarkGfm],
remarkPlugins: ['remark-gfm'],
},
})

Expand Down
88 changes: 88 additions & 0 deletions knowledge/design_system/clarity_design_system.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@

Each step represents a **specific UI intent**, not just a color.

---

## Core Principles

### 1. Perceptual Consistency
- Steps must feel evenly spaced to the human eye
- Do NOT use linear interpolation blindly
- Avoid muddy midtones or sudden jumps

Bad:
- 400 → 500 barely changes
- 700 → 800 jumps too much

Good:
- Smooth, predictable progression

---

### 2. Intent-Based Design (VERY IMPORTANT)


| Step | Token | Category | Usage Description |
|------|------|---------------------------|------------------|
| 1 | 50 | Backgrounds | App/page background, lowest visual weight |
| 2 | 100 | Backgrounds | Subtle surfaces, section backgrounds |
| 3 | 200 | Interactive components | Hover backgrounds, subtle interaction states |
| 4 | 300 | Interactive components | Active/pressed states, stronger surface contrast |
| 5 | 400 | Interactive components | Selected states, emphasized surfaces |
| 6 | 500 | Borders & separators | Light borders, dividers |
| 7 | 600 | Borders & separators | Default borders, stronger separators |
| 8 | 700 | Solid colors | Disabled text/icons, muted UI elements
| 9 | 800 | Solid colors | supporting content
Comment on lines +34 to +35
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix malformed table rows (missing trailing pipe).

Line 34 and Line 35 are missing the closing |, so markdown table rendering/linting can break.

Proposed fix
-| 8    | 700  | Solid colors              | Disabled text/icons, muted UI elements
-| 9    | 800  | Solid colors              | supporting content
+| 8    | 700  | Solid colors              | Disabled text/icons, muted UI elements |
+| 9    | 800  | Solid colors              | Supporting content |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| 8 | 700 | Solid colors | Disabled text/icons, muted UI elements
| 9 | 800 | Solid colors | supporting content
| 8 | 700 | Solid colors | Disabled text/icons, muted UI elements |
| 9 | 800 | Solid colors | Supporting content |
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 34-34: Table pipe style
Expected: leading_and_trailing; Actual: leading_only; Missing trailing pipe

(MD055, table-pipe-style)


[warning] 35-35: Table pipe style
Expected: leading_and_trailing; Actual: leading_only; Missing trailing pipe

(MD055, table-pipe-style)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@knowledge/design_system/clarity_design_system.md` around lines 34 - 35, The
two markdown table rows starting with "| 8    | 700  | Solid colors             
| Disabled text/icons, muted UI elements" and "| 9    | 800  | Solid colors     
| supporting content" are missing the trailing pipe; update these rows in
clarity_design_system.md by appending a closing "|" to each line so the table
cells are properly terminated (i.e., ensure both rows end with " |").

| 10 | 900 | Accessible text | Secondary text, high readability |
| 11 | 1000 | Accessible text | Headings,Primary text, max contrast text (near black) |
---
Comment on lines +37 to +38
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a blank line after the table block.

Line 38 should be separated from the table by a blank line to satisfy markdown table boundary rules.

Proposed fix
-| 11   | 1000 | Accessible text           | Headings,Primary text, max contrast text (near black) |
----
+| 11   | 1000 | Accessible text           | Headings, Primary text, max contrast text (near black) |
+
+---
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| 11 | 1000 | Accessible text | Headings,Primary text, max contrast text (near black) |
---
| 11 | 1000 | Accessible text | Headings, Primary text, max contrast text (near black) |
---
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 37-37: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@knowledge/design_system/clarity_design_system.md` around lines 37 - 38, The
Markdown table ending with the row "| 11   | 1000 | Accessible text           |
Headings,Primary text, max contrast text (near black) |" needs a blank line
added immediately after it to satisfy Markdown table boundary rules; edit the
document to insert one empty line following that table block so the subsequent
content (line 38) is separated from the table.


### 3. No Pure Extremes

- ❌ No `#ffffff`
- ❌ No `#000000`

Why?
Pure colors feel harsh and break layering.

Instead:
- 50 → neutral white — no hue tint (off-white, not `#ffffff`)
- 1000 → neutral near-black — no hue tint (not `#000000`)

---

### 4. Neutral Only

- No hue tint (including blue/green/purple); chroma stays at zero
- True grayscale only: neutral white through neutral near-black

If you want semantic colors, define them separately.

---

### 5. Accessibility First

- Text (900–1000) must meet **WCAG AA**
- Borders (400–500) must be visible but not distracting
- Avoid low contrast adjacent steps

---

## Reference Scale (Baseline)

This is the default Rad UI grayscale.

```json
{
"50": "#fafafa",
"100": "#f4f4f5",
"200": "#e4e4e7",
"300": "#d4d4d8",
"400": "#a1a1aa",
"500": "#71717a",
"600": "#52525b",
"700": "#3f3f46",
"800": "#27272a",
"900": "#18181b",
"1000": "#0f0f11"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,4 +308,4 @@
"eslint": "$eslint"
}
}
}
}
126 changes: 79 additions & 47 deletions src/components/tools/SandboxEditor/SandboxEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import React, { PropsWithChildren, useEffect, useState } from 'react';
import Button from '~/components/ui/Button/Button';
import Separator from '~/components/ui/Separator/Separator';
import Heading from '~/components/ui/Heading/Heading';
import Select from '~/components/ui/Select/Select';
import Text from '~/components/ui/Text/Text';

import RovingFocusGroup from '~/core/utils/RovingFocusGroup';

import Theme from '~/components/ui/Theme/Theme';

import colors from '~/design-systems/clarity/tokens/colors';
Expand All @@ -22,25 +21,35 @@ const RadUILogo = () => {
return <svg xmlns="http://www.w3.org/2000/svg" width="50" height="15" viewBox="0 0 951 275" fill="none"><path d="M211.75 272.493C194.441 267.63 180.91 259.376 166.776 245.07C145.884 223.912 136.769 201.743 136.804 172.243C136.857 141.767 146.647 117.84 167.751 96.5938C187.507 76.6964 209.675 67.5021 237.998 67.4666C262.471 67.4311 279.638 73.892 295.83 89.2099L305.265 98.138V87.2397C305.265 74.176 306.63 73.6967 318.317 82.6248C327.238 89.4407 337.914 102.877 344.192 115.195C353.556 133.549 353.733 135.111 354.46 204.441L355.152 269.529L350.168 270.115C347.42 270.417 335.396 270.434 323.443 270.133L301.718 269.6V259.926C301.718 254.619 301.292 250.271 300.778 250.271C300.263 250.271 295.67 253.59 290.58 257.637C274.123 270.736 264.741 273.966 241.42 274.57C227.445 274.925 218.117 274.268 211.75 272.493ZM270.044 224.818C281.518 219.191 290.669 209.801 296.77 197.43C301.806 187.188 302.604 183.727 302.604 172.207C302.604 161.185 301.735 157.102 297.657 148.76C284.693 122.366 255.963 109.658 228.971 118.373C216.752 122.331 202.316 135.519 195.931 148.6C182.701 175.651 195.789 210.156 224.537 224.001C234.078 228.598 236.721 229.095 249.401 228.563C258.357 228.19 265.965 226.806 270.044 224.818ZM456.203 272.316C429.938 265.251 404.791 243.845 392.43 218.055C383.474 199.382 380.778 185.609 381.966 164.593C383.58 136.069 394.505 112.622 414.988 93.6651C435.028 75.1167 460.353 66.2241 488.338 67.8571C509.23 69.0995 521.661 74.3002 534.608 87.2575C540.3 92.9551 545.302 97.6233 545.709 97.6233C546.117 97.6233 546.454 75.6492 546.454 48.8116V0H573.943H601.431V134.898V269.795H573.943H546.454V260.033C546.454 254.673 545.709 250.271 544.787 250.271C543.865 250.271 540.07 253.111 536.363 256.59C522.655 269.405 505.559 275.173 481.847 274.996C472.998 274.943 461.47 273.736 456.203 272.316ZM516.022 223.504C527.603 217.718 535.973 208.967 543.049 195.247C546.383 188.786 547.199 184.384 547.252 172.172C547.341 158.292 546.862 156.197 541.294 145.902C534.945 134.205 527.851 127.496 514.532 120.591C503.129 114.663 481.954 114.627 468.901 120.485C458.065 125.348 446.644 137.045 441.235 148.778C435.631 160.936 435.755 185.928 441.466 196.667C456.877 225.634 488.782 237.136 516.022 223.504ZM750.206 269.156C742.776 266.955 732.632 262.536 727.648 259.341C706.633 245.851 688.81 219.351 684.908 195.815C683.613 187.951 683.081 161.646 683.4 120.698L683.897 57.6865L702.908 57.1895L721.902 56.6748L722.416 124.638L722.913 192.584L727.488 202.364C736.019 220.54 752.618 231.935 773.315 233.817C795.217 235.805 818.041 222.634 827.228 202.737C830.934 194.714 831.129 191.501 831.98 126.023L832.867 57.6865L851.932 57.1895L870.996 56.6748L870.943 126.413C870.908 193.862 870.784 196.507 866.935 207.778C858.192 233.479 842.089 252.223 819.034 263.53C797.114 274.286 773.918 276.185 750.206 269.156ZM19.171 270.31L0 269.671L0.638443 205.399C1.24142 144.518 1.47197 140.471 5.23169 129.2C10.8358 112.391 24.2963 93.0084 36.8878 83.6543L47.2271 75.9687L47.7059 81.9148C47.9719 85.1808 48.6104 89.7602 49.1247 92.0854C50.0469 96.1856 50.3129 96.0791 58.0274 88.3402C62.4078 83.9738 70.3529 78.1696 75.691 75.4717C86.5977 69.9515 109.138 66.1886 119.566 68.1588L126.146 69.3835V93.0084V116.633L109.741 117.698C87.0056 119.189 80.8872 121.514 69.2888 133.069C62.0354 140.294 58.8964 144.997 57.389 150.872C56.1653 155.683 55.1012 180.621 54.6933 213.458C54.3032 243.473 53.647 268.393 53.2036 268.837C51.6784 270.363 38.2002 270.949 19.171 270.31ZM929.946 270.168L911.75 269.671L912.211 163.67L912.673 57.6865L931.737 57.1895L950.802 56.6748V163.137C950.802 221.676 950.199 269.831 949.472 270.133C948.745 270.434 939.948 270.452 929.946 270.168Z" fill="currentColor"/></svg>;
};

type ColorSelectProps = {color:typeof colors[keyof typeof colors], colorName: string, changeAccentColor: (colorName: keyof typeof colors)=>void}
type ColorSelectProps = {
color:typeof colors[keyof typeof colors],
isDarkMode: boolean,
isSelected: boolean
}

const ColorSelect = ({ color, colorName, changeAccentColor }: ColorSelectProps) => {
const dimensions = 32;
const ColorSelect = ({ color, isDarkMode, isSelected }: ColorSelectProps) => {
const dimensions = 18;
return <span
aria-label={`Change accent color to ${colorName}`}
className='cursor-pointer rounded-full inline-flex'
style={{ width: dimensions, height: dimensions, backgroundColor: color.light['900'] }}
aria-hidden="true"
className='inline-flex rounded-full border transition-all duration-150 ease-out'
style={{
width: dimensions,
height: dimensions,
backgroundColor: isDarkMode ? color.dark['900'] : color.light['900'],
borderColor: isSelected ? 'var(--rad-ui-text-primary)' : 'transparent',
boxShadow: isSelected ? '0 0 0 2px var(--rad-ui-surface-panel)' : 'none'
}}
></span>;
};

type SandboxProps = {className?: string | ''} & PropsWithChildren
type SandboxProps = { className?: string } & PropsWithChildren

const SandboxEditor = ({ children, className } : SandboxProps) => {
const SandboxEditor = ({ children, className }: SandboxProps) => {
const [isDarkMode, setIsDarkMode] = useState(false);

type AvailableColors = keyof typeof colors

const [colorName, setColorName] = useState<AvailableColors>('plum');
const [colorName, setColorName] = useState<AvailableColors>('gray');

useEffect(() => {
const isDarkMode = localStorage.getItem('isDarkMode') === 'true';
Expand All @@ -55,49 +64,72 @@ const SandboxEditor = ({ children, className } : SandboxProps) => {
return <Theme
appearance={isDarkMode ? 'dark' : 'light'}
accentColor={colorName}>
<div className={'p-4 shadow-sm text-gray-900 min-h-screen border border-gray-300 bg-gray-50'}>
<div className='mb-4'>
{/* @ts-ignore */}
<div className='flex items-center space-x-4'>
<div className='text-gray-1000'>
<RadUILogo/>
<div className='min-h-screen border border-gray-300 bg-gray-50 p-3 shadow-sm text-gray-900 sm:p-4'>
<div className='mb-2'>
<div className='flex items-start justify-between gap-2'>
<div className='flex min-w-0 items-center space-x-3'>
<div className='text-gray-1000 shrink-0'>
<RadUILogo/>
</div>
<Separator orientation='vertical' />
<div className='min-w-0'>
<Heading as='h1' className='text-3xl font-semibold leading-none tracking-tight text-gray-1000 sm:text-4xl'>
Sandbox Editor
</Heading>
<Text className='mt-0.5 text-sm font-normal leading-tight text-gray-950'>
Preview Rad UI components with theme and accent controls.
</Text>
</div>
</div>
<Separator orientation='vertical' />
<Button variant="solid" onClick={toggleDarkMode}>{isDarkMode ? <SunIcon/> : <MoonIcon/>}</Button>
<Button
variant="solid"
aria-label={`Switch to ${isDarkMode ? 'light' : 'dark'} mode`}
onClick={toggleDarkMode}
>
{isDarkMode ? <SunIcon/> : <MoonIcon/>}
</Button>
</div>
<Separator />
<div>
<div className="flex items-center space-x-4">
<Heading as='h1' className="text-gray-1000">Sandbox Editor</Heading>
<Separator orientation='vertical' />
<Text className="font-normal text-gray-950">
Customize the colors of the Rad UI components by clicking on the color swatches below.
</Text>
<div className='flex flex-col gap-1.5 py-1.5 sm:flex-row sm:items-center sm:justify-between'>
<Text as='span' className='text-sm text-gray-950 leading-none'>
Accent: <span className='capitalize'>{colorName}</span>
</Text>
<div className='w-full sm:w-[220px]'>
<Select.Root value={colorName} onValueChange={(value) => setColorName(value as AvailableColors)}>
<Select.Trigger aria-label='Accent color'>
<span className='flex items-center gap-2'>
<ColorSelect
color={colors[colorName]}
isDarkMode={isDarkMode}
isSelected={true}
/>
<span className='capitalize'>{colorName}</span>
</span>
</Select.Trigger>
<Select.Portal>
<Select.Content>
<Select.Group>
{Object.entries(colors).map(([availableColorName, color]) => (
<Select.Item key={availableColorName} value={availableColorName}>
<span className='flex items-center gap-2'>
<ColorSelect
color={color}
isDarkMode={isDarkMode}
isSelected={availableColorName === colorName}
/>
<span className='capitalize'>{availableColorName}</span>
</span>
</Select.Item>
))}
</Select.Group>
</Select.Content>
</Select.Portal>
</Select.Root>
</div>
<Separator />
<RovingFocusGroup.Root >
<RovingFocusGroup.Group className='flex space-x-1 my-1'>
{Object.keys(colors).map((color, idx) => {
const colorName = color as AvailableColors;
return <RovingFocusGroup.Item
key={idx}
className='cursor-pointer rounded-full inline-block w-8 h-8 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
onFocus={() => setColorName(colorName)}
onClick={() => setColorName(colorName)}
>
<button>
<ColorSelect colorName={color} color={colors[colorName]} changeAccentColor={setColorName}/>
</button>
</RovingFocusGroup.Item>;
}
)}
</RovingFocusGroup.Group>

</RovingFocusGroup.Root>
</div>
</div>
<Separator/>
<div className={`${className}`} >
<div className={`pt-2.5 ${className ?? ''}`.trim()} >
{children}
</div>
</div>
Expand Down
Loading
Loading