10 KiB
Panel & Modal UI Style Guide
This guide documents the visual patterns used in OpenNoodl's editor panels and modals. Always follow these patterns when creating new UI components.
Core Principles
1. Professional, Not Playful
- NO emojis in UI labels, buttons, or headers
- NO decorative icons unless they serve a functional purpose
- Clean, minimal aesthetic that respects the user's intelligence
- Think "developer tool" not "consumer app"
2. Consistent Visual Language
- Use design tokens (CSS variables) for ALL colors
- Consistent spacing using the spacing system (4px base unit)
- Typography hierarchy using the Text component types
- All interactive elements must have hover/active states
3. Dark Theme First
- Design for dark backgrounds (
--theme-color-bg-2,--theme-color-bg-3) - Ensure sufficient contrast with light text
- Colored elements should be muted, not neon
Panel Structure
Standard Panel Layout
<div className={css.Root}>
{/* Header with title and close button */}
<div className={css.Header}>
<HStack hasSpacing>
<Icon icon={IconName.Something} size={IconSize.Small} />
<VStack>
<Text textType={TextType.DefaultContrast}>Panel Title</Text>
<Text textType={TextType.Shy} style={{ fontSize: '11px' }}>
Subtitle or context
</Text>
</VStack>
</HStack>
<IconButton icon={IconName.Close} onClick={onClose} />
</div>
{/* Toolbar with actions */}
<div className={css.Toolbar}>{/* Filters, search, action buttons */}</div>
{/* Content area (scrollable) */}
<div className={css.Content}>{/* Main panel content */}</div>
{/* Footer (optional - pagination, status) */}
<div className={css.Footer}>{/* Page controls, counts */}</div>
</div>
Panel CSS Pattern
.Root {
display: flex;
flex-direction: column;
height: 100%;
background-color: var(--theme-color-bg-2);
color: var(--theme-color-fg-default);
}
.Header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid var(--theme-color-bg-3);
flex-shrink: 0;
}
.Toolbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 16px;
background-color: var(--theme-color-bg-3);
gap: 8px;
flex-shrink: 0;
}
.Content {
flex: 1;
overflow-y: auto;
padding: 16px;
}
.Footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 16px;
border-top: 1px solid var(--theme-color-bg-3);
background-color: var(--theme-color-bg-2);
flex-shrink: 0;
}
Modal Structure
Standard Modal Layout
<div className={css.Overlay}>
<div className={css.Modal}>
{/* Header */}
<div className={css.Header}>
<Text textType={TextType.Proud}>Modal Title</Text>
<IconButton icon={IconName.Close} onClick={onClose} />
</div>
{/* Body */}
<div className={css.Body}>{/* Form fields, content */}</div>
{/* Footer with actions */}
<div className={css.Footer}>
<PrimaryButton label="Cancel" variant={PrimaryButtonVariant.Muted} onClick={onClose} />
<PrimaryButton label="Create" variant={PrimaryButtonVariant.Cta} onClick={onSubmit} />
</div>
</div>
</div>
Modal CSS Pattern
.Overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.Modal {
background-color: var(--theme-color-bg-2);
border-radius: 8px;
width: 480px;
max-width: 90vw;
max-height: 80vh;
display: flex;
flex-direction: column;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
.Header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid var(--theme-color-bg-3);
}
.Body {
padding: 20px;
overflow-y: auto;
flex: 1;
}
.Footer {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 16px 20px;
border-top: 1px solid var(--theme-color-bg-3);
}
Form Elements
Text Inputs
.Input {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--theme-color-bg-3);
border-radius: 4px;
background-color: var(--theme-color-bg-1);
color: var(--theme-color-fg-default);
font-size: 13px;
font-family: inherit;
&:focus {
outline: none;
border-color: var(--theme-color-primary);
}
&::placeholder {
color: var(--theme-color-fg-default-shy);
}
}
Select Dropdowns
.Select {
padding: 8px 12px;
border: 1px solid var(--theme-color-bg-3);
border-radius: 4px;
background-color: var(--theme-color-bg-1);
color: var(--theme-color-fg-default);
font-size: 13px;
min-width: 120px;
&:focus {
outline: none;
border-color: var(--theme-color-primary);
}
}
Form Groups
.FormGroup {
margin-bottom: 16px;
&:last-child {
margin-bottom: 0;
}
}
.Label {
display: block;
margin-bottom: 6px;
font-size: 12px;
font-weight: 500;
color: var(--theme-color-fg-default);
}
.HelpText {
margin-top: 4px;
font-size: 11px;
color: var(--theme-color-fg-default-shy);
}
Data Tables & Grids
Table Pattern
.Grid {
width: 100%;
border-collapse: collapse;
font-size: 13px;
th {
text-align: left;
padding: 10px 12px;
font-weight: 500;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--theme-color-fg-default-shy);
background-color: var(--theme-color-bg-3);
border-bottom: 1px solid var(--theme-color-bg-3);
position: sticky;
top: 0;
z-index: 1;
}
td {
padding: 8px 12px;
border-bottom: 1px solid var(--theme-color-bg-3);
color: var(--theme-color-fg-default);
}
tr:hover td {
background-color: var(--theme-color-bg-3);
}
}
Type Badges
For showing data types, use subtle colored badges:
.TypeBadge {
display: inline-block;
padding: 2px 6px;
border-radius: 3px;
font-size: 10px;
font-weight: 500;
color: white;
}
// Use semantic colors, not hardcoded
.TypeString {
background-color: var(--theme-color-primary);
}
.TypeNumber {
background-color: var(--theme-color-success);
}
.TypeBoolean {
background-color: var(--theme-color-notice);
}
.TypeDate {
background-color: #8b5cf6;
} // Purple - no token available
.TypePointer {
background-color: var(--theme-color-danger);
}
Expandable Rows
For tree-like or expandable content:
.ExpandableRow {
border: 1px solid var(--theme-color-bg-3);
border-radius: 6px;
margin-bottom: 8px;
overflow: hidden;
background-color: var(--theme-color-bg-2);
&[data-expanded='true'] {
border-color: var(--theme-color-primary);
}
}
.RowHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
cursor: pointer;
transition: background-color 0.15s;
&:hover {
background-color: var(--theme-color-bg-3);
}
}
.RowContent {
padding: 0 16px 16px;
background-color: var(--theme-color-bg-1);
border-top: 1px solid var(--theme-color-bg-3);
}
.ExpandIcon {
transition: transform 0.2s;
color: var(--theme-color-fg-default-shy);
}
Empty States
When there's no content to show:
.EmptyState {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 48px 24px;
text-align: center;
color: var(--theme-color-fg-default-shy);
.EmptyIcon {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.5;
}
.EmptyText {
margin-bottom: 8px;
}
}
Loading & Error States
Loading
.Loading {
display: flex;
align-items: center;
justify-content: center;
padding: 48px;
color: var(--theme-color-fg-default-shy);
}
Error
.Error {
padding: 12px 16px;
background-color: rgba(239, 68, 68, 0.1);
border: 1px solid var(--theme-color-danger);
border-radius: 4px;
color: var(--theme-color-danger);
font-size: 13px;
}
Button Patterns
Use PrimaryButton Variants Correctly
| Variant | Use For |
|---|---|
Cta |
Primary action (Create, Save, Submit) |
Muted |
Secondary action (Cancel, Close, Refresh) |
Ghost |
Tertiary action (Edit, View, minor action) |
Danger |
Destructive action (Delete) |
Button Sizing
Small- In toolbars, table rows, compact spacesMedium- Modal footers, standalone actionsLarge- Rarely used, hero actions only
Spacing System
Use consistent spacing based on 4px unit:
| Token | Value | Use For |
|---|---|---|
xs |
4px | Tight spacing, icon gaps |
sm |
8px | Related elements |
md |
12px | Standard padding |
lg |
16px | Section padding |
xl |
24px | Large gaps |
xxl |
32px | Major sections |
Typography
Use Text Component Types
| Type | Use For |
|---|---|
Proud |
Panel titles, modal headers |
DefaultContrast |
Primary content, item names |
Default |
Body text, descriptions |
Shy |
Secondary text, hints, metadata |
Font Sizes
- Headers: 14-16px
- Body: 13px
- Labels: 12px
- Small text: 11px
- Badges: 10px
Don'ts
❌ Don't use emojis in buttons or labels ❌ Don't use hardcoded colors - always use CSS variables ❌ Don't use bright/neon colors - keep it muted ❌ Don't use decorative icons that don't convey meaning ❌ Don't use rounded corners > 8px - keep it subtle ❌ Don't use shadows > 0.4 opacity - stay subtle ❌ Don't use animation duration > 200ms - keep it snappy ❌ Don't mix different styling approaches - be consistent
Reference Components
For working examples, see:
packages/noodl-editor/src/editor/src/views/panels/schemamanager/packages/noodl-editor/src/editor/src/views/panels/databrowser/packages/noodl-editor/src/editor/src/views/panels/BackendServicesPanel/
Last Updated: January 2026