CSSuicontrols¶
CSS-styled HTML-backed UI controls for MATLAB uifigure apps. Each component is a uihtml element whose content is a self-contained HTML+CSS+JS document — this gets you modern, themeable, animated controls that look native-web without shelling out to JavaScript yourself.
All components share a common base class (CSSBase), a consistent name-value-pair constructor surface, live property patching (no page reload on property assignment), and a small set of reusable styling presets.
Components¶
Class |
What it is |
|---|---|
|
Push button with optional SVG icon, configurable icon position, shape (rectangle/square/circle), and text-hide-below-width |
|
Static text label with selectable alignment and rich styling |
|
Select/dropdown list |
|
Single-line text input |
|
Numeric input with min/max/step |
|
Toggle switch |
|
Single boolean checkbox with custom-CSS tick |
|
Radio group (single-select) or checkbox group (multi-select), horizontal or vertical |
|
Multi-line text input |
|
Scrollable multi-select list |
|
Pill-shaped search input with embedded submit button |
|
Filterable hierarchical tree, fast on 10k+ nodes |
|
Tabular display with optional inline cell editing ( |
|
Horizontal progress bar with optional text label (above/below/on) and tick marks |
|
Animated progress bar driven by |
Plus two supporting classes:
Class |
Purpose |
|---|---|
|
Abstract parent — handles lifecycle: temp-file writing, JS bridge injection, Enabled-state queuing, CSS variable compilation, live CSS patching |
|
Pure-data container holding a set of style variables (Color, BackgroundColor, etc.) plus a CSS string. Static factory methods build named presets |
And two demo entry points:
Script |
Shows |
|---|---|
|
Core components in a grid, with preset switching, enable-toggle, and callback wiring |
|
Progress bar variations including |
How it works¶
Each component writes a self-contained HTML file to a temp directory, points a uihtml element at it, and then communicates bidirectionally:
MATLAB → HTML: property setters send tiny JSON messages through the
uihtml.Databridge. The HTML side has a small JS handler that patches the relevant DOM nodes (text, attributes, classes, CSS variables) — never a full reload.HTML → MATLAB: user interactions (click, change, hover) fire
uihtml.DataChangedFcnwith a JSON payload.CSSBaseroutes that to the subclass’s appropriate MATLAB callback (e.g.,ButtonPushedFcn,ValueChangedFcn).
This architecture is what makes live property patching cheap — the round trip for a text update is a single JSON event, no rebuild.
CSS schema¶
Every component’s HTML follows the same selector convention so one preset can style all of them. Two layers of class:
Widget-type classes (one per component)¶
Every widget also tags its #css-root with a kebab-case widget-type class. Pick one when you want a rule to apply to one kind of widget only — e.g. tint button backgrounds without touching inputs.
Class |
Component |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Choose the smallest selector that does the job: role classes for behavior shared across kinds (hover lift, disabled fade), widget-type classes for kind-specific tweaks (button background vs input background).
Presets¶
CSSPreset is a set of named style bundles you can drop into any component via the 'Style' name-value pair:
btn = CSSuiButton(parent, 'Style', 'shadow'); % by name
btn = CSSuiButton(parent, 'Style', CSSPreset.shadow()); % by object
btn.setStyle('neon'); % switch at runtime
Available presets (static factories on CSSPreset):
Preset |
Look |
|---|---|
|
Neumorphic raised-shadow (default) |
|
Same shadow language on a near-white base |
|
Clean flat modern |
|
Frosted-glass / glassmorphism |
|
Cyberpunk dark with glowing cyan borders |
|
Rounded pill with solid purple accent |
|
Dark-mode flat (VS Code style) |
Call CSSPreset.list() for the authoritative enumerated list. Presets set convenience properties (Color, BackgroundColor, BorderRadius, FontSize, …) and append a CSS string targeting the schema selectors above.
One preset, multiple widget types¶
Because each widget tags its root with a .cssui-<kebab> class (see schema above), a single CSSPreset can style buttons one way and inputs another from the same .CSS string:
p = CSSPreset.shadow_light();
p.CSS = [p.CSS ...
'.cssui-button .css-surface { background-color: #ececec; }' ...
'.cssui-edit .css-surface,' ...
'.cssui-numeric .css-surface,' ...
'.cssui-textarea .css-surface { background-color: #ffffff; }'];
Apply that one preset to every widget in your app and they share a unified text color, font, and shadow language while still having kind-specific backgrounds. This is the pattern an app should reach for when it wants a single source of truth for its visual identity.
Customizing a preset¶
Build a custom preset by copying one and tweaking convenience properties or appending CSS rules:
p = CSSPreset.flat();
p.Color = '#2a7a2a';
p.BorderRadius = '12px';
btn = CSSuiButton(parent, 'Style', p);
Common constructor surface¶
Every component accepts these via CSSBase:
Name |
Meaning |
|---|---|
|
|
|
logical — greyed/disabled state (default |
|
preset name or |
|
raw CSS string appended to the compiled document |
|
path to a |
|
where the generated HTML gets written (default: |
Plus component-specific options (Text, Value, Icon, Items, etc.) — see each class’s docstring for the full list.
CSS cascade order¶
CSS rules land in this order inside every widget’s <style> block (later rules win at equal specificity):
External
.cssfile (CSSFileproperty, if set).Schema CSS —
#css-rootsizing,.css-disableddefaults.:root { --color, --bg-color, ... }— convenience-property variables.The component’s own internal
<style>(its baseline appearance).<style id="cssbase-override">— preset CSS first, thenobj.CSS(user CSS).
Practical consequence: the component’s defaults are easy to override with a preset, and a user’s obj.CSS always wins over the preset at equal specificity. No !important needed for most overrides.
Usage example¶
fig = uifigure('Position', [100 100 400 200]);
gl = uigridlayout(fig, [3, 2]);
% Pick a preset once — applies consistently across components
preset = CSSPreset.dark();
lbl = CSSuiLabel(gl, 'Text', 'Volume:', 'Style', preset);
sld = CSSuiNumericField(gl, 'Value', 50, 'Min', 0, 'Max', 100, 'Style', preset, ...
'ValueChangedFcn', @(s,e) fprintf('Volume = %d\n', s.Value));
btn = CSSuiButton(gl, 'Text', 'Apply', 'Icon', 'check.svg', 'Style', preset, ...
'ButtonPushedFcn', @(s,e) disp('Applied'));
% Live update — no page reload
btn.Text = 'Updated';
btn.setStyle('neon');
For a full demo: run CSSDemo or CSSProgressBarDemo at the MATLAB prompt.
Files in this directory¶
Component classes (
.mfiles):CSSuiButton,CSSuiLabel,CSSuiDropdown,CSSuiEditField,CSSuiNumericField,CSSuiSwitch,CSSuiCheckbox,CSSuiRadioGroup,CSSuiTextArea,CSSuiListBox,CSSuiSearchBar,CSSuiTree,CSSuiTable,CSSUIProgressBar,SmoothProgressBarCSSBase.m— abstract lifecycle managerCSSPreset.m— style preset factoryCSSDemo.m,CSSProgressBarDemo.m— runnable demosREADME.md— this file
Dependencies¶
Base MATLAB uifigure support (R2020b+ recommended — uihtml exists in R2019b, but DataChangedFcn routing is more reliable in R2020b and later). No toolboxes required. No external libraries — every component is a standalone HTML+CSS+JS document.