This page compares vgui features against HTML+CSS item by item, helping developers quickly build a comprehensive understanding of vgui’s capability boundaries.
Support level markers: ✅ Full support · 🔶 Partial support / differences · ❌ Not supported
vgui’s view! macro accepts lowercase tags matching HTML names, mapping them to gpui elements under the hood. Container elements have no default styling; text elements carry corresponding default font styles.
HTML Elements vgui Mapping Notes
div span p header footer nav main section article aside address form fieldset legend figure figcaption pre blockquote qgpui::div()No default styling, pure containers
h1–h6 strong b em i u s del strike mark small code kbd samp var cite abbr dfn bdi bdo timegpui::div()With corresponding default font styles
ul ol li dl dt ddflex column / bold / padding-left No list-style; must manually draw numbers/bullets
adiv().cursor_pointer() + blue texthref attribute accepted but not navigable; use on:click for navigation
buttondiv().cursor_pointer()Default tabindex=0
imggpui::img(src)Supports object_fit (fill/contain/cover/scale-down/none); alt accepted but not used for a11y
svggpui::svg().path(src)—
br hr wbrempty line / separator / empty void elements
table thead tbody tfoot tr td th captionflex layout simulation colspan→flex_grow; rowspan has no visual effect
colgroup col datalist option optgroupcompiles but renders empty or special-purpose no-op elements
canvasvgui::canvas_element(paint)<canvas paint={|ctx| ...}> with Context2D 2D drawing API; supports style/class, no children/events
HTML/CSS vgui Support
No native component system (relies on frameworks) Uppercase tags call functions/structs, attribute→field mapping ✅
Attribute spreading {..props} → Rust struct update syntax / Spread<E> trait✅
Attribute name mapping conventions: on:click→on_click, type→r#type, ref→r#ref.
HTML/CSS vgui Support
No built-in conditional/list rendering (relies on framework v-if/v-for, React conditional rendering, etc.) <Show when={}> / <For each={}> / <Switch>+<Match> / <Index each={}>, all support fallback✅
HTML/CSS Property css! SupportDifferences
display (flex/block/none/grid)✅ —
visibility✅ —
overflow (+-x/-y)✅ —
position (relative/absolute)🔶 No fixed/sticky
flex-direction flex-wrap flex flex-grow flex-shrink flex-basis✅ —
justify-content align-items align-self align-content✅ —
gap (+row-gap column-gap)✅ gap does not support more than 2 values
grid-template-columns grid-template-rows✅ No fr unit; use numeric column counts
grid-column grid-row (+-start/-end)✅ —
grid-template-areas✅ String literals define named areas; auto-infers column/row counts
grid-area✅ "name" resolves against grid-template-areas, or N / row / col / four values
scrollbar-width✅ —
HTML/CSS Property css! SupportDifferences
width height min-* max-*✅ —
padding (+-inline/-block)✅ —
margin (+-inline/-block)✅ —
inset top right bottom left✅ —
aspect-ratio✅ —
HTML/CSS Property css! SupportDifferences
background (color + linear-gradient)✅ —
background-color color opacity✅ —
border (+per-side/-color/-style/-width)✅ —
border-radius (+per-corner)✅ —
cursor box-shadow✅ —
HTML/CSS Property css! SupportDifferences
font-size✅ Does not support %
font-weight (named + numeric 100–900)✅ —
font-style font-family✅ —
text-align✅ left/center/right
text-decoration (+-color/-thickness/-style)✅ —
text-overflow text-background white-space line-height line-clamp✅ —
Capability Support Differences
--name: value definition✅ Inside css!
var(--name, fallback) reference✅ —
theme! macro + set_theme()✅ Global thread-local theme
{expr} runtime expression interpolation✅ —
CSS Property Reason Alternative
transform / translate / rotate / scalegpui has no div transforms ❌ None
transition / animation (inside css!)No equivalent in gpui styling model Use tw! transition-*/animate-* classes
z-index (inside css!)No equivalent in gpui styling model Use tw! z-N utilities
box-sizinggpui uses content-box semantics ❌ None
outlineNo equivalent Use border
list-style / list-style-typeNo list-style rendering Manually draw numbers/bullets
background-imageNo equivalent Use linear-gradient
background-position / background-size / background-repeatNo equivalent ❌ None
float / clearNo equivalent ❌ None
@media queries (inside css!)css! does not support media queriesUse tw! responsive prefixes sm:/md:/lg:/xl:
!importantNo equivalent ❌ None
position: fixed / stickygpui has no fixed/sticky positioning ❌ None
Tailwind Category Support Differences
Display: flex/block/hidden/grid/inline-flex ✅ inline-flex same as flex
Flex: direction/wrap/grow/shrink ✅ —
Justify / Align ✅ —
Position: relative/absolute/static ✅ No fixed/sticky
Overflow ✅ —
Spacing: p/m/gap full range ✅ spacing scale 0–96
Sizing: w/h/min/max ✅ —
Colors: 22 palettes × 11 shades + black/white/transparent ✅ —
Typography: weight/size/family/line-height/decoration/overflow/style/white-space ✅ —
Borders: width/style/color/radius ✅ —
Shadows: sm–2xl/none ✅ —
Cursor / Opacity (0–100) / Inset ✅ —
Grid: grid-cols-N/grid-rows-N/col-N/row-N ✅ —
Aspect ratio / Line clamp (1–10) ✅ —
Z-index: z-0–z-50/z-auto ✅ —
Arbitrary values: [Npx]/[Nrem]/[N%]/[#hex]/[rgb()]/[rgba()] ✅ —
Opacity modifier: /NN ✅ —
CSS Pseudo-Class vgui Support Differences
:hoverhover={css!{}} attribute / hover: prefix✅
:focusfocus={css!{}} attribute / focus: prefix✅
:activeactive={css!{}} attribute / active: prefix✅
:focus-within :focus-visible :visited :link :target :nth-child etc.❌ No equivalent
Responsive breakpoints @media (min-width: …) sm:/md:/lg:/xl: prefixes✅ Applied at runtime based on viewport width
Responsive breakpoint thresholds (matching Tailwind defaults):
Prefix Min Width
sm:≥ 640px
md:≥ 768px
lg:≥ 1024px
xl:≥ 1280px
CSS animation/transition vgui Support
@keyframesBuilt-in animate-pulse/bounce/ping ✅
animate-spingpui has no rotation transform ❌
Custom animations animate={|el| el.with_animation(...)} closure✅
transitiontransition/transition-opacity/transition-colors/transition-all✅
duration-* / ease-*— ✅
delay-*Parsed and stored but no effect 🔶
HTML/CSS vgui Support
No native variant system (relies on libraries like CVA) variants! macro generates enum + composite struct + ApplyStyle impl✅
HTML/CSS vgui Support
CSS flexbox full-featured Based on gpui flexbox ✅
flex-direction/wrap/grow/shrink/basis/justify/align/gap all fully supported.
HTML/CSS Grid vgui Support
grid-template-columns/rows (column/row counts)✅ —
grid-column/row (+-start/-end, span N)✅ —
grid-template-areas + grid-area✅ thread-local stack parses named areas
fr unit / minmax() / repeat() / auto-fit / auto-fill❌ Use numeric column counts
grid-auto-flow / grid-auto-columns / grid-auto-rows❌ No equivalent
HTML/CSS vgui Support
position: relative / absolute✅ —
position: fixed / sticky❌ No equivalent
top/right/bottom/left/inset✅ —
HTML/CSS vgui Support
table semantic layoutflex simulation 🔶
colspanflex_grow✅
rowspanNo visual effect ❌
Automatic column width from content Must manually specify w-[Npx] 🔶
HTML/CSS vgui Support
@media queriestw! responsive prefixes sm:/md:/lg:/xl:✅ Runtime viewport width check, not compile-time
Programmatic breakpoint query Breakpoint::from_width(width) + reactive::get_viewport_width()🔶 No dedicated use_breakpoint() hook; must combine manually
CSS Container Queries ❌ No equivalent
css! macro does not support @media; responsive capabilities are limited to tw! utilities. The breakpoint.rs module doc-comment mentions a use_breakpoint() hook, but that function is not implemented or exported. The actual approach is to combine Breakpoint::from_width() with reactive::get_viewport_width() to obtain the current breakpoint.
HTML input type vgui Support Differences
text/password/search/email/url/telFull-featured text input (cursor/selection/clipboard/IME) ✅
numberText input + numeric validation + min/max/step ✅
date/datetime-local/time/month/weekCalendar popup ✅
colorPreset color palette popup 🔶 No free RGB sliders
checkbox18×18 rounded box + ✓ ✅
radioroving tabindex (arrow key navigation within radiogroup) ✅
rangeDraggable slider ✅
fileFile picker 🔶 multiple supported, accept unused
submit/button/resetClickable button ✅ Auto-binds on:submit/on:reset inside form
hiddenEmpty render ✅
textareaMulti-line text input ✅ rows attribute
selectDropdown popup ✅ options/groups/multiple/custom option content rendering
datalistAutocomplete suggestions ✅ list=id association
labelfor= explicit association / wrapping association✅
formon:submit/on:reset + Enter to submit✅
input type="image" / standalone <button> element / <output>— 🔶 <output> is only a div alias; no image input
HTML Event vgui Event Support
clickon:click✅ Also click() helper function for simplified signature
keydown / keyupon:keydown / on:keyup✅
pointerdown / pointerup / pointermoveon:pointerdown/up/move✅
dblclickon:dblclick✅
contextmenuon:contextmenu✅
scroll / wheelon:scroll / on:wheel✅
resizeon:resize✅
modifiers_changedon:modifiers_changed✅
mouse_down_out / mouse_up_outon:mouse_down_out / on:mouse_up_out✅ gpui-specific
any_mouse_downon:any_mouse_down✅ gpui-specific
input / changeon:input / on:change✅ Signature varies by input type
submit / reseton:submit / on:reset✅ Form context
closeon:close✅ Dialog-specific
focus/blur/mouseenter/mouseleave/load/Drag events/touch events❌ gpui pointer events cover some
HTML ARIA vgui Support
role attributerole="button" etc., maps to accesskit::Role✅
aria:labelaria:label="..."✅
aria:description aria:keyshortcuts aria:selected aria:expanded— ✅
aria:toggled aria:valuenow/aria:numeric_value aria:value aria:placeholder— ✅
aria:numeric_value_step— ✅
role/aria:* on input/select/textarea/label/formMust use wrapping element 🔶
HTML native a11y (alt attribute) img alt accepted but not used for a11y🔶
accesskit integration gpui exposes accessibility tree via accesskit ✅
Web Framework Pattern vgui Support
React useState create_signal (SolidJS-style fine-grained)✅
React useMemo create_memo✅
React useEffect create_effect (synchronous execution, not async)✅
React useEffect cleanup on_cleanup (runs on scope disposal)✅
Dependency tracking Automatic fine-grained tracking ✅
React Context API Context<T> + <Provider> + use_context✅
Re-runs entire app() closure on each render Not SolidJS compile-time fine-grained, but slot model keeps state persistent 🔶
useReducer / useRefUse signal + NodeRef as replacement 🔶 No direct equivalent
HTML Element vgui Support
<dialog> (HTML5)<dialog>✅ Portal rendering, focus trap, focus restore, click-outside, Escape to close
Custom modal/portal <portal priority={N}>✅
tooltip/popover positioning <floating position={...}>✅ Automatic overflow avoidance
<details>/<summary><details open={}> + <summary>✅ open is a controlled prop
<progress><progress value={} max={}>✅
<meter><meter value={} min={} max={} low={} high={} optimum={}>✅
HTML Canvas API vgui Context2D Support
fillRect / strokeRectfill_rect / stroke_rect✅
clearRectclear_rect🔶 No-op (immediate mode; repaints from blank each frame)
beginPath / moveTo / lineTo / closePathbegin_path / move_to / line_to / close_path✅
quadraticCurveTo / bezierCurveToquadratic_curve_to / bezier_curve_to✅
arcarc✅ Flattened to line segments (max(2, ceil(sweep/(π/16))))
fill / strokefill / stroke✅
fillTextfill_text✅ y is baseline; parses CSS font shorthand
strokeTextstroke_text❌ No-op (gpui has no text outline API)
measureTextmeasure_text → TextMetrics { width }✅
save / restoresave / restore✅
translate / rotate / scaletranslate / rotate / scale✅
setTransform / resetTransformset_transform / reset_transform✅
fillStyle / strokeStylefill_style / stroke_style (Hsla)🔶 Solid colors only; no gradients/patterns
lineWidthline_width✅
fontfont (CSS shorthand, e.g. "16px sans-serif")✅
textAligntext_align (CanvasTextAlign)✅ Start/Left/Center/Right/End
globalAlphaglobal_alpha✅ Clamped 0–1
lineCap / lineJoin— ❌ lyon types not re-exported from gpui
createLinearGradient / createRadialGradient / createPattern— ❌ No gradient/pattern support
drawImage— ❌ Requires RenderImage (not exposed)
clip— ❌ gpui content mask is axis-aligned bounds only
putImageData / getImageData / createImageData— ❌ No pixel-level access
shadowBlur / shadowColor / shadowOffset*— ❌ No shadow on paths (use box-shadow on wrapping element)
Canvas events (mousemove, click on canvas) — 🔶 Wrap <canvas> in <div on:click={...}>
Runtime CSS color parser color() supports #rgb/#rgba/#rrggbb/#rrggbbaa, rgb(), rgba(), hsl(), hsla(), 140+ named colors, and "transparent".
See Canvas Example for a live demo.
HTML/CSS vgui Support
No native SPA routing (relies on frameworks) router module✅
API overview:
create_router(initial) → Router (signal-driven)
match_pattern("/users/:id", path) → RouteMatch with params
:param placeholder + * wildcard
build_path(pattern, params) reverse building
router.navigate(cx, path) navigation
router.render(cx, &[(pattern, callback)]) declarative route matching
See Router for details.
HTML DOM ref / React ref vgui NodeRef Support
focus / is_focused / contains_focused— ✅
bounds / scroll_offset / scroll_to / scroll_to_top / scroll_to_bottom / set_scroll_offset— ✅
child_bounds / child_count— ✅
Full DOM handle Based on gpui FocusHandle + ScrollHandle; data from previous frame 🔶
Direct ref on select/textarea/text input/range Use wrapping div 🔶
HTML/CSS vgui Support
CSS Custom Properties (--var) --name: value + var(--name) inside css!✅
CSS theme switching theme! macro + set_theme() global thread-local✅
Automatic reactive theme changes Must read signal inside render closure + set_theme to trigger re-render 🔶
CSS media query theme switching Combine breakpoint + signal manually 🔶
HTML/CSS vgui Support
Cross-platform (browser) Native + WASM dual-target ✅
Linux: Wayland/X11
macOS: Cocoa/Metal
Windows: Win32/DirectX
Web: wasm32-unknown-unknown + wasm-bindgen
GPU-accelerated rendering (gpui)
Feature Alternative
transform / translate / rotate / scale❌ None
transition / animation (inside css!)tw! transition-* / animate-*
z-index (inside css!)tw! z-N
box-sizing❌ None (content-box semantics)
outlineborder
list-style / list-style-typeManually draw numbers/bullets
background-imagelinear-gradient
background-position / background-size / background-repeat❌ None
float / clear❌ None
@media queries (inside css!)tw! responsive prefixes sm:/md:/lg:/xl:
!important❌ None
position: fixed / sticky❌ None
animate-spin❌ None (no rotation transform)
:focus-within / :focus-visible / :visited / :link / :target / :nth-child etc.❌ None
CSS Container Queries ❌ None
focus / blur / mouseenter / mouseleave / load / Drag / touch eventsgpui pointer events cover some
Grid fr / minmax() / repeat() / auto-fit / auto-fill / grid-auto-* Numeric column counts
rowspan visual effect❌ None
input type="image"❌ None
Canvas strokeText / drawImage / clip / gradients / patterns / pixel ops ❌ See Canvas 2D Drawing for details
Canvas lineCap / lineJoin ❌ lyon types not re-exported
This section summarizes the alignment between book documentation and actual source code, for developer reference.
Already-documented features (this page aggregates and compares them; they are not new discoveries):
Source vs documentation discrepancies :
use_breakpoint() hook : The crates/vgui/src/breakpoint.rs module doc-comment (line 3) mentions a use_breakpoint() hook, but that function is neither implemented nor exported. The actual approach to obtain the current breakpoint is to combine Breakpoint::from_width() with reactive::get_viewport_width(). The book only documents the tw! responsive prefixes and does not cover this programmatic API.
css! does not support @media : CSS Property Reference lists @media as an unsupported css! property and points to tw! responsive prefixes. This description is accurate — the css! macro itself does not support media queries; responsive capabilities are provided by tw!.