Styling Guide
SableUI uses a Tailwind-inspired styling system with chainable modifers. Styles are applied inline where elements are delcared, eliminating the need for verbose styling or seperate stylesheet files.
Philosophy
Traditional UI frameowkrs seperate structure and style, but SableUI brings styles directly to your component definition, just like the powerful tailwind + react stack, without the overhead.
Example:
Div(bg(90, 160, 255) p(12) rounded(8) w(120)) {
Text("Click me", textColour(255, 255, 255) fontSize(16));
}
Core Concepts
Modifiers (like w(200)) return references to a hidden ElementInfo object, allowing chaining in any order:
Div(w(200) h(100) bg(45, 45, 45) p(10) m(5) rounded(8))
Each modifies is a macro that calls a setter method:
w(200)→.setWidth(200)bg(45, 45, 45)→.setBgColour(Colour(45, 45, 45))
You can find a list of the modifers and definitions in SableUI/SableUI.h or in this table here.
Spacing Units
All spacing values are in pixels. There are no relative units (%) currently and isn't planned anytime soon.
Div(w(300) h(200)) // 300px wide, 200px tall
Layout Properties
Width & Height
// Fixed dimensions
Div(w(200) h(100))
// Fill available space (shared evenly across other 'fill' siblings)
Div(w_fill h_fill)
// Fit content (default for most elements)
Div(w_fit h_fit)
// Constraints
Div(minW(100) maxW(500) minH(50) maxH(300))
Width/Height Types:
w(value)/h(value)- Fixed size in pixelsw_fill/h_fill- Fill available spacew_fit/h_fit- Fit to content sizeminW/maxW/minH/maxH- Size constrains in pixels
Example:
Div(w_fill h_fit minH(200) maxH(600)) {
// Takes full width, height fits content
// but constrained between 200-600px
}
Margin & Padding
// All sides
Div(m(10) p(20))
// Horizontal/Vertical
Div(mx(15) my(10) px(20) py(15))
// Individual sides
Div(mt(5) mr(10) mb(5) ml(10))
Div(pt(5) pr(10) pb(5) pl(10))
Example:
Div(bg(200, 200, 200) m(20) p(15)) {
// 20px margin (transparent space outside)
// 15px padding (gray space inside, before content)
Text("Content");
}
Layout Direction
Controls how child elements flow
// Vertical (default)
Div(up_down) { // Top to bottom
Text("First");
Text("Second");
}
Div(down_up) { // Bottom to top
Text("First");
Text("Second");
}
// Horizontal
Div(left_right) { // Left to right
Text("First");
Text("Second");
}
Div(right_left) { // Right to left
Text("First");
Text("Second");
}
Default: up_down (top to bottom)
Centering
// Center horizontally
Div(centerX) {
Text("Centered");
}
// Center vertically
Div(centerY) {
Text("Centered");
}
// Center both axes
Div(centerXY) {
Text("Centered");
}
Note: Centering applies to the element within its parent, not to the element's children.
Example
Div(w(500) h(500) bg(255, 0, 0)) {
// Rect is centered within its parent
Rect(w(50) h(50) centerXY bg(0, 255, 0))
}
Colours
The bg (background colour) modifier can use RGB or RGBA values:
Colour Format: bg(r, g, b, a) where:
r,g,b- Red, Green, Blue (0-255)a- Alhpa/opacity (0-255, default 255 (opaque))
// Background color
Div(bg(45, 45, 45)) // RGB
Div(bg(45, 45, 45, 200)) // RGBA (with alpha)
The same rules go with the textColour property:
// Text color
Text("Hello", textColour(255, 255, 255))
Text("Faded", textColour(200, 200, 200, 128))
And you can use the rgb/rgba modifier for added flexibility.
Example with inline conditional colours:
// Using rgb/rgba helpers
Div(bg(value == true ? rgb(255, 45, 45) : rgb(45, 255, 45)))
Div(bg(rgba(45, 45, 45, 200)))
Border Radius
// All corners
Div(rounded(8))
// Sharp corners
Div(rounded(0))
// Pill shape
Div(w(100) h(40) rounded(20))
Note: Individual corner radii are yet to be added. (v1.0).
Text Properties
Font Size
Text("Small", fontSize(10))
Text("Normal", fontSize(14))
Text("Large", fontSize(24))
Text("Huge", fontSize(48))
Default: 11px
Line Height
Controls spacing between lines of wrapped text:
Text("Multi-line text that will wrap...",
fontSize(14)
lineHeight(1.5) // 1.5x the font size
)
Default: 1.15 (15% taller than font size)
Text Justification
Text("Left aligned", justify_left)
Text("Centered", justify_center)
Text("Right aligned", justify_right)
Default: justify_left
Text Wrapping
// Wrap text (default)
Text("Long text that will wrap...", maxW(200) textWrap(true))
// No wrapping (minimum size is constrained to one line)
Text("Long text that won't wrap...", maxW(200) textWrap(false))
Text Styling
Use string methods for bold, italic, etc.
Learn more about how SableString functions and how why/how .bold() is implmented/required here.
Example with formatting:
SableString message =
SableString("This is ") +
SableString("super cool").bold() +
SableString(" formatting").italic();
TextU32(message);
Output: This is super cool formatting
This will probably me improved with macros
Absolute Positioning
Div(absolutePos(100, 50)) {
Text("At x:100, y:50");
}
[!WARNING] Absolute positioning can mess up the layout tree significantly. For most use cases that require absolute positioning, it is recomended you use them with a
CustomTargetQueuewhich is seperate to the default element tree. You can find out more about it in Advanced Topics and can view documented implmentations of specific use cases like that require absolute positioning and custom render targets like modals.