0
# UI & Styling
1
2
This module provides comprehensive styling infrastructure for Superset applications, including the theme system with Emotion integration, color scheme management, text dimension calculations, and layout utilities. It enables consistent styling across components with support for theming, color scales, and responsive design.
3
4
## Overview
5
6
The UI & styling system is built around three core areas: the theme system powered by Emotion for CSS-in-JS styling, the color management system with support for categorical and sequential color schemes, and the dimension utilities for text measurement and layout calculations.
7
8
## Theme System
9
10
### Core Theme Integration { .api }
11
12
Emotion-based styling system with comprehensive theme support:
13
14
```typescript
15
import {
16
ThemeProvider,
17
useTheme,
18
styled,
19
css,
20
jsx,
21
withTheme,
22
SupersetTheme,
23
SupersetThemeProps,
24
supersetTheme,
25
emotionCache,
26
EmotionCacheProvider,
27
createEmotionCache
28
} from '@superset-ui/core';
29
30
// Theme provider for React context
31
const ThemeProvider: React.ComponentType<{ theme: SupersetTheme; children: React.ReactNode }>;
32
33
// Hook to access theme context
34
function useTheme(): SupersetTheme;
35
36
// Styled components factory
37
const styled: typeof emotionStyled;
38
39
// CSS template literal function
40
const css: typeof emotionCss;
41
42
// JSX pragma for Emotion
43
const jsx: typeof emotionJsx;
44
45
// Higher-order component for theme access
46
const withTheme: typeof emotionWithTheme;
47
48
// Emotion cache providers
49
const EmotionCacheProvider: typeof CacheProvider;
50
function createEmotionCache(options?: { key?: string }): EmotionCache;
51
52
// Default emotion cache instance
53
const emotionCache: EmotionCache;
54
```
55
56
### SupersetTheme Interface { .api }
57
58
Comprehensive theme structure with design tokens:
59
60
```typescript
61
interface SupersetTheme {
62
// Layout and spacing
63
borderRadius: number;
64
gridUnit: number;
65
transitionTiming: number;
66
67
// Color system
68
colors: {
69
text: {
70
label: string;
71
help: string;
72
};
73
primary: ColorScale;
74
secondary: ColorScale;
75
grayscale: ColorScale;
76
error: ColorScale;
77
warning: ColorScale;
78
alert: ColorScale;
79
success: ColorScale;
80
info: ColorScale;
81
};
82
83
// Typography system
84
typography: {
85
families: {
86
sansSerif: string;
87
serif: string;
88
monospace: string;
89
};
90
weights: {
91
light: number;
92
normal: number;
93
medium: number;
94
bold: number;
95
};
96
sizes: {
97
xxs: number;
98
xs: number;
99
s: number;
100
m: number;
101
l: number;
102
xl: number;
103
xxl: number;
104
};
105
};
106
107
// Opacity levels
108
opacity: {
109
light: string;
110
mediumLight: string;
111
mediumHeavy: string;
112
heavy: string;
113
};
114
115
// Z-index management
116
zIndex: {
117
aboveDashboardCharts: number;
118
dropdown: number;
119
max: number;
120
};
121
}
122
123
// Color scale structure
124
interface ColorScale {
125
base: string;
126
dark1: string;
127
dark2: string;
128
light1: string;
129
light2: string;
130
light3?: string;
131
light4?: string;
132
light5?: string;
133
}
134
135
// Component props interface for themed components
136
interface SupersetThemeProps {
137
theme: SupersetTheme;
138
}
139
140
// Default theme instance
141
const supersetTheme: SupersetTheme;
142
```
143
144
## Color Management
145
146
### Color Scheme System { .api }
147
148
Comprehensive color scheme management for data visualization:
149
150
```typescript
151
import {
152
ColorScheme,
153
CategoricalScheme,
154
SequentialScheme,
155
CategoricalColorScale,
156
getCategoricalSchemeRegistry,
157
getSequentialSchemeRegistry,
158
BRAND_COLOR
159
} from '@superset-ui/core';
160
161
// Base color scheme class
162
class ColorScheme {
163
readonly id: string;
164
readonly label: string;
165
readonly colors: string[];
166
readonly isDefault?: boolean;
167
168
constructor(config: {
169
id: string;
170
label: string;
171
colors: string[];
172
isDefault?: boolean;
173
});
174
175
getColors(numColors?: number): string[];
176
}
177
178
// Categorical color scheme for discrete data
179
class CategoricalScheme extends ColorScheme {
180
constructor(config: CategoricalSchemeConfig);
181
}
182
183
// Sequential color scheme for continuous data
184
class SequentialScheme extends ColorScheme {
185
constructor(config: SequentialSchemeConfig);
186
}
187
188
// Categorical color scale for data mapping
189
class CategoricalColorScale {
190
constructor(colors: string[], forced?: boolean);
191
192
getColor(key: string): string;
193
setColor(key: string, color: string): this;
194
getColorMap(): { [key: string]: string };
195
copy(): CategoricalColorScale;
196
}
197
198
// Registry access functions
199
function getCategoricalSchemeRegistry(): RegistryWithDefaultKey<CategoricalScheme>;
200
function getSequentialSchemeRegistry(): RegistryWithDefaultKey<SequentialScheme>;
201
202
// Brand color constant
203
const BRAND_COLOR = '#00A699';
204
```
205
206
### Color Namespace Management { .api }
207
208
Advanced color management with namespaces for consistent color assignment:
209
210
```typescript
211
import { CategoricalColorNamespace, getSharedLabelColor } from '@superset-ui/core';
212
213
// Categorical color namespace utilities
214
namespace CategoricalColorNamespace {
215
function getNamespace(namespace?: string): CategoricalColorScale;
216
function getScale(schemeId?: string, namespace?: string): CategoricalColorScale;
217
function setColor(key: string, color: string, namespace?: string): void;
218
function getColor(key: string, schemeId?: string, namespace?: string): string;
219
}
220
221
// Shared label color management
222
function getSharedLabelColor(): SharedLabelColor;
223
224
class SharedLabelColor {
225
getColorMap(scheme?: string, namespace?: string): { [key: string]: string };
226
addSlice(label: string, color: string, scheme?: string, namespace?: string): void;
227
removeSlice(label: string, scheme?: string, namespace?: string): void;
228
clear(scheme?: string, namespace?: string): void;
229
}
230
```
231
232
## Text and Layout Utilities
233
234
### Text Dimension Calculation { .api }
235
236
Utilities for measuring text dimensions and calculating font sizes:
237
238
```typescript
239
import {
240
getTextDimension,
241
getMultipleTextDimensions,
242
computeMaxFontSize,
243
Dimension,
244
TextStyle
245
} from '@superset-ui/core';
246
247
// Single text measurement
248
function getTextDimension(config: {
249
text: string;
250
style?: TextStyle;
251
}): Dimension;
252
253
// Multiple text measurements (more efficient for batch operations)
254
function getMultipleTextDimensions(config: {
255
texts: string[];
256
style?: TextStyle;
257
}): Dimension[];
258
259
// Calculate maximum font size that fits constraints
260
function computeMaxFontSize(config: {
261
text: string;
262
idealFontSize: number;
263
maxWidth: number;
264
maxHeight: number;
265
style?: TextStyle;
266
}): number;
267
268
// Dimension interface
269
interface Dimension {
270
width: number;
271
height: number;
272
}
273
274
// Text style configuration
275
interface TextStyle {
276
fontSize?: string | number;
277
fontFamily?: string;
278
fontWeight?: string | number;
279
fontStyle?: string;
280
letterSpacing?: string | number;
281
lineHeight?: string | number;
282
}
283
```
284
285
### Layout Utilities { .api }
286
287
Utilities for margin handling and CSS length parsing:
288
289
```typescript
290
import { mergeMargin, parseLength, Margin } from '@superset-ui/core';
291
292
// Merge margin objects with proper handling of undefined values
293
function mergeMargin(...margins: (Partial<Margin> | undefined)[]): Margin;
294
295
// Parse CSS length values to numeric values
296
function parseLength(input: string | number): number;
297
298
// Margin interface
299
interface Margin {
300
top: number;
301
right: number;
302
bottom: number;
303
left: number;
304
}
305
```
306
307
## Usage Examples
308
309
### Basic Theme Setup
310
311
```typescript
312
import React from 'react';
313
import {
314
ThemeProvider,
315
supersetTheme,
316
emotionCache,
317
EmotionCacheProvider
318
} from '@superset-ui/core';
319
320
// App-level theme setup
321
const App: React.FC = ({ children }) => (
322
<EmotionCacheProvider value={emotionCache}>
323
<ThemeProvider theme={supersetTheme}>
324
{children}
325
</ThemeProvider>
326
</EmotionCacheProvider>
327
);
328
329
// Custom theme configuration
330
const customTheme = {
331
...supersetTheme,
332
colors: {
333
...supersetTheme.colors,
334
primary: {
335
...supersetTheme.colors.primary,
336
base: '#FF6B6B' // Custom primary color
337
}
338
}
339
};
340
```
341
342
### Styled Components with Theme
343
344
```typescript
345
import React from 'react';
346
import { styled, useTheme, css } from '@superset-ui/core';
347
348
// Basic styled component with theme access
349
const StyledButton = styled.button`
350
background-color: ${({ theme }) => theme.colors.primary.base};
351
color: ${({ theme }) => theme.colors.grayscale.light5};
352
border: none;
353
border-radius: ${({ theme }) => theme.borderRadius}px;
354
padding: ${({ theme }) => theme.gridUnit * 2}px ${({ theme }) => theme.gridUnit * 4}px;
355
font-family: ${({ theme }) => theme.typography.families.sansSerif};
356
font-size: ${({ theme }) => theme.typography.sizes.m}px;
357
font-weight: ${({ theme }) => theme.typography.weights.medium};
358
transition: all ${({ theme }) => theme.transitionTiming}s ease;
359
360
&:hover {
361
background-color: ${({ theme }) => theme.colors.primary.dark1};
362
}
363
364
&:disabled {
365
opacity: ${({ theme }) => theme.opacity.mediumHeavy};
366
cursor: not-allowed;
367
}
368
`;
369
370
// Component using theme hook
371
const ThemedCard: React.FC<{ children: React.ReactNode }> = ({ children }) => {
372
const theme = useTheme();
373
374
return (
375
<div
376
css={css`
377
background: ${theme.colors.grayscale.light5};
378
border: 1px solid ${theme.colors.grayscale.light2};
379
border-radius: ${theme.borderRadius}px;
380
padding: ${theme.gridUnit * 4}px;
381
box-shadow: 0 ${theme.gridUnit}px ${theme.gridUnit * 3}px ${theme.colors.grayscale.light2};
382
`}
383
>
384
{children}
385
</div>
386
);
387
};
388
```
389
390
### Color Scheme Usage
391
392
```typescript
393
import {
394
getCategoricalSchemeRegistry,
395
CategoricalColorNamespace,
396
CategoricalColorScale
397
} from '@superset-ui/core';
398
399
// Register custom color scheme
400
const customColors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8'];
401
const customScheme = new CategoricalScheme({
402
id: 'custom',
403
label: 'Custom Colors',
404
colors: customColors
405
});
406
407
getCategoricalSchemeRegistry().registerValue('custom', customScheme);
408
409
// Use color scale for data visualization
410
const createColorScale = (data: string[]) => {
411
const scale = CategoricalColorNamespace.getScale('custom', 'myChart');
412
413
// Assign colors to data categories
414
data.forEach(category => {
415
CategoricalColorNamespace.getColor(category, 'custom', 'myChart');
416
});
417
418
return scale;
419
};
420
421
// Get colors for chart data
422
const chartData = ['Category A', 'Category B', 'Category C'];
423
const colorScale = createColorScale(chartData);
424
const colors = chartData.map(category => colorScale.getColor(category));
425
```
426
427
### Text Measurement and Font Sizing
428
429
```typescript
430
import {
431
getTextDimension,
432
computeMaxFontSize,
433
getMultipleTextDimensions
434
} from '@superset-ui/core';
435
436
// Measure single text element
437
const textDimension = getTextDimension({
438
text: 'Chart Title',
439
style: {
440
fontSize: 16,
441
fontFamily: 'Inter, Arial, sans-serif',
442
fontWeight: 'bold'
443
}
444
});
445
446
console.log(`Text size: ${textDimension.width} x ${textDimension.height}`);
447
448
// Calculate optimal font size for container
449
const optimalFontSize = computeMaxFontSize({
450
text: 'Long chart title that needs to fit',
451
idealFontSize: 18,
452
maxWidth: 200,
453
maxHeight: 30,
454
style: {
455
fontFamily: 'Inter, Arial, sans-serif',
456
fontWeight: 'bold'
457
}
458
});
459
460
// Batch text measurement for better performance
461
const labels = ['Label 1', 'Label 2', 'Label 3', 'Very Long Label Name'];
462
const labelDimensions = getMultipleTextDimensions({
463
texts: labels,
464
style: { fontSize: 12, fontFamily: 'Inter' }
465
});
466
467
// Find maximum width for layout calculations
468
const maxLabelWidth = Math.max(...labelDimensions.map(d => d.width));
469
```
470
471
### Advanced Styling Patterns
472
473
```typescript
474
import { styled, css, useTheme } from '@superset-ui/core';
475
476
// Conditional styling based on props
477
interface CardProps {
478
variant?: 'primary' | 'secondary' | 'danger';
479
size?: 'small' | 'medium' | 'large';
480
}
481
482
const StyledCard = styled.div<CardProps>`
483
${({ theme }) => css`
484
border-radius: ${theme.borderRadius}px;
485
padding: ${theme.gridUnit * 2}px;
486
font-family: ${theme.typography.families.sansSerif};
487
transition: all ${theme.transitionTiming}s ease;
488
`}
489
490
${({ theme, variant = 'primary' }) => {
491
const colorMap = {
492
primary: theme.colors.primary,
493
secondary: theme.colors.secondary,
494
danger: theme.colors.error
495
};
496
497
const colors = colorMap[variant];
498
499
return css`
500
background-color: ${colors.light4};
501
border: 1px solid ${colors.light2};
502
color: ${colors.dark2};
503
`;
504
}}
505
506
${({ theme, size = 'medium' }) => {
507
const sizeMap = {
508
small: theme.gridUnit * 2,
509
medium: theme.gridUnit * 4,
510
large: theme.gridUnit * 6
511
};
512
513
return css`
514
padding: ${sizeMap[size]}px;
515
font-size: ${theme.typography.sizes[size === 'small' ? 's' : size === 'large' ? 'l' : 'm']}px;
516
`;
517
}}
518
`;
519
520
// Dynamic styling with CSS variables
521
const DynamicStyledComponent = styled.div`
522
${({ theme }) => {
523
// Set CSS custom properties from theme
524
return css`
525
--primary-color: ${theme.colors.primary.base};
526
--secondary-color: ${theme.colors.secondary.base};
527
--border-radius: ${theme.borderRadius}px;
528
--grid-unit: ${theme.gridUnit}px;
529
530
background: var(--primary-color);
531
border-radius: var(--border-radius);
532
padding: calc(var(--grid-unit) * 2);
533
534
.nested-element {
535
background: var(--secondary-color);
536
margin: var(--grid-unit);
537
}
538
`;
539
}}
540
`;
541
```
542
543
### Responsive Design with Theme
544
545
```typescript
546
import { styled, css } from '@superset-ui/core';
547
548
// Responsive breakpoints (not in theme but commonly used)
549
const breakpoints = {
550
xs: '0px',
551
sm: '576px',
552
md: '768px',
553
lg: '992px',
554
xl: '1200px'
555
};
556
557
const media = {
558
xs: `@media (min-width: ${breakpoints.xs})`,
559
sm: `@media (min-width: ${breakpoints.sm})`,
560
md: `@media (min-width: ${breakpoints.md})`,
561
lg: `@media (min-width: ${breakpoints.lg})`,
562
xl: `@media (min-width: ${breakpoints.xl})`
563
};
564
565
// Responsive styled component
566
const ResponsiveGrid = styled.div`
567
${({ theme }) => css`
568
display: grid;
569
gap: ${theme.gridUnit * 2}px;
570
padding: ${theme.gridUnit * 2}px;
571
572
${media.xs} {
573
grid-template-columns: 1fr;
574
}
575
576
${media.sm} {
577
grid-template-columns: repeat(2, 1fr);
578
}
579
580
${media.md} {
581
grid-template-columns: repeat(3, 1fr);
582
}
583
584
${media.lg} {
585
grid-template-columns: repeat(4, 1fr);
586
}
587
`}
588
`;
589
```
590
591
### Color Utilities and Helpers
592
593
```typescript
594
import {
595
CategoricalColorNamespace,
596
getSharedLabelColor,
597
BRAND_COLOR
598
} from '@superset-ui/core';
599
600
// Shared color management across dashboard
601
const labelColorManager = getSharedLabelColor();
602
603
// Add colors for specific data labels
604
labelColorManager.addSlice('Sales', '#FF6B6B', 'd3Category10', 'dashboard_1');
605
labelColorManager.addSlice('Marketing', '#4ECDC4', 'd3Category10', 'dashboard_1');
606
607
// Get consistent colors across charts
608
const getConsistentColor = (label: string, namespace: string) => {
609
return CategoricalColorNamespace.getColor(label, 'd3Category10', namespace);
610
};
611
612
// Clear colors when dashboard changes
613
const clearDashboardColors = (namespace: string) => {
614
labelColorManager.clear('d3Category10', namespace);
615
};
616
617
// Use brand color for primary elements
618
const brandButton = styled.button`
619
background-color: ${BRAND_COLOR};
620
color: white;
621
`;
622
```
623
624
### Layout Margin Utilities
625
626
```typescript
627
import { mergeMargin, parseLength } from '@superset-ui/core';
628
629
// Merge multiple margin configurations
630
const baseMargin = { top: 10, right: 15, bottom: 10, left: 15 };
631
const titleMargin = { top: 20, bottom: 5 };
632
const finalMargin = mergeMargin(baseMargin, titleMargin);
633
// Result: { top: 20, right: 15, bottom: 5, left: 15 }
634
635
// Parse CSS length values
636
const numericValue1 = parseLength('16px'); // 16
637
const numericValue2 = parseLength('1.5em'); // Converted to pixels based on context
638
const numericValue3 = parseLength(20); // 20
639
640
// Calculate responsive margins
641
const calculateResponsiveMargin = (baseSize: number) => {
642
const sm = parseLength(`${baseSize * 0.5}px`);
643
const md = parseLength(`${baseSize}px`);
644
const lg = parseLength(`${baseSize * 1.5}px`);
645
646
return { sm, md, lg };
647
};
648
```
649
650
## Related Documentation
651
652
- [Core Models & Utilities](./core-models.md) - Registry system for color schemes
653
- [Data Formatting](./data-formatting.md) - Formatter integration with styling
654
- [Plugin System](./plugin-system.md) - Chart component styling
655
- [Dashboard Components](./dashboard.md) - Dashboard layout and styling