Deep Dive

CSS Styling in Oorian

A complete guide to CSS styling in Oorian—from inline styles and internal stylesheets to dynamic, programmatic CSS generation.

M. WarbleFebruary 10, 202613 min read
CSS Styling in Oorian

Oorian gives you full control over CSS styling without ever leaving Java. Whether you need quick inline styles on a single element, a block of internal CSS on a page, a linked external stylesheet, or a fully dynamic stylesheet built in memory, Oorian has a clean, type-safe API for it. This article walks through all four approaches and when to use each one.

Inline CSS

Inline styles are applied directly to individual elements. Oorian provides two complementary APIs: the CssStyle object and the type-safe methods inherited from StyledElement.

StyledElement Methods

Every Oorian HTML element that extends StyledElement inherits type-safe CSS setter methods. These map directly to CSS properties and use enums where appropriate, so your IDE catches typos at compile time:

Div card = new Div();
card.setDisplay(Display.FLEX);
card.setFlexDirection(FlexDirection.COLUMN);
card.setBackgroundColor(Color.WHITE);
card.setPadding(24);
card.setBorderRadius(8);
card.setBoxShadow(0, 2, 8, 0, new Color(0, 0, 0));

Button submit = new Button("Submit");
submit.setBackgroundColor("#2563eb");
submit.setColor(Color.WHITE);
submit.setCursor(Cursor.POINTER);
submit.setPadding(10, 24);
submit.setBorderRadius(6);
submit.setFontWeight(FontWeight.WEIGHT_600);

You also get CSS class management methods on every StyledElement:

card.addClass("shadow-lg rounded");
card.removeClass("hidden");

// State-based classes for interactive elements
button.setNormalClass("btn-default");
button.setOverClass("btn-hover");
button.setPressedClass("btn-active");
button.setDisabledClass("btn-disabled");

The CssStyle Object

For more advanced scenarios, you can build a CssStyle object and apply it to an element. This is useful when you want to define a style once and reuse it across multiple elements, or when you want to merge styles together:

// Define a reusable style
CssStyle cardStyle = new CssStyle();
cardStyle.setBackgroundColor(Color.WHITE);
cardStyle.setPadding(24);
cardStyle.setBorderRadius(8);
cardStyle.setBoxShadow(0, 2, 8, 0, new Color(0, 0, 0));

// Apply to multiple elements
panel1.setStyle(cardStyle);
panel2.setStyle(cardStyle);

// Merge additional properties
CssStyle highlightStyle = new CssStyle();
highlightStyle.setBorderLeft("4px solid #2563eb");
panel1.addStyle(highlightStyle);

State-based styles let Oorian automatically swap styles on hover, press, selection, and disabled states—all from Java:

CssStyle normal = new CssStyle();
normal.setBackgroundColor("#2563eb");
normal.setColor(Color.WHITE);

CssStyle hover = new CssStyle();
hover.setBackgroundColor("#1d4ed8");

button.setNormalStyle(normal);
button.setOverStyle(hover);

When to use inline CSS: Dynamic values that change at runtime (progress bars, status colors), one-off styling on individual elements, and state-based style swaps.

Internal CSS

Internal CSS is embedded directly in the page using an HTML <style> element. In Oorian, the Style class represents this element. You can pass it a raw CSS string or a CssStyleSheet object (covered in the Dynamic CSS section below).

@Override
protected void createHead(Head head)
{
    super.createHead(head);

    Style style = new Style();
    style.setContent(".card { background: #fff; border-radius: 8px; padding: 24px; }"
        + ".card:hover { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); }"
        + ".badge { display: inline-block; padding: 4px 12px; border-radius: 999px; font-size: 12px; }");
    head.addElement(style);
}

Internal styles are ideal for page-specific CSS that needs selectors, pseudo-classes, or pseudo-elements that inline styles cannot express. However, for anything beyond a few simple rules, consider using the Dynamic CSS approach described below, which provides type safety and better maintainability.

When to use internal CSS: Page-specific styles that require selectors or pseudo-classes, small amounts of CSS scoped to a single page.

External CSS

External CSS links a separate stylesheet file to your page. Use head.addCssLink() to add a <link> element pointing to the stylesheet URL:

@Override
protected void createHead(Head head)
{
    super.createHead(head);
    head.addCssLink("/css/app-theme.css");
    head.addCssLink("/css/components.css");
}

This is the traditional approach for static CSS files that you author by hand and deploy alongside your application. The browser caches these files, so they only download once.

When to use external CSS: Global styles shared across many pages, third-party CSS libraries, static stylesheets that benefit from browser caching.

Dynamic CSS

This is where Oorian really shines. Instead of writing CSS by hand in string literals or static files, you can build entire stylesheets programmatically in Java using Oorian's CSS classes: CssStyleSheet, CssRule, CssMediaQuery, KeyFrames, and CssFile. Every CSS property is a type-safe method call. Your IDE provides autocomplete, and the compiler catches errors before you ever see a browser.

Building a Stylesheet with CssStyleSheet and CssRule

A CssStyleSheet is a container for CSS rules. A CssRule combines one or more selectors with style declarations:

CssStyleSheet sheet = new CssStyleSheet();

// A simple rule
CssRule card = new CssRule(".card");
card.setBackgroundColor(Color.WHITE);
card.setPadding(24);
card.setBorderRadius(8);
card.setBoxShadow(0, 2, 8, 0, new Color(0, 0, 0));
sheet.addRule(card);

// Pseudo-class rule
CssRule cardHover = new CssRule(".card:hover");
cardHover.setBoxShadow(0, 4, 16, 0, new Color(0, 0, 0));
cardHover.setTransform("translateY(-2px)");
sheet.addRule(cardHover);

// Multiple selectors on one rule
CssRule headings = new CssRule("h1", "h2", "h3");
headings.setColor("#1f2937");
headings.setFontWeight(FontWeight.WEIGHT_700);
sheet.addRule(headings);

CSS Combinators Without the Cryptic Syntax

If you have ever stared at .nav > ul + li ~ a and tried to remember which symbol means what, you are not alone. CSS combinator syntax is notoriously easy to forget. Oorian replaces those symbols with descriptive methods on CssRule:

CSS Syntax Oorian Method Meaning
.parent .child (space) addDescendant() Any nested element at any depth
.parent > .child addChild() Direct children only
.item + .item addAdjacentSibling() Immediately following sibling
.item ~ .item addGeneralSibling() All following siblings

Build nested selectors by composing rules together:

// Style links inside a nav bar: .navbar a
CssRule navbar = new CssRule(".navbar");
navbar.setDisplay(Display.FLEX);
navbar.setAlignItems(AlignItems.CENTER);
navbar.setPadding(16);

CssRule navLink = new CssRule("a");
navLink.setColor(Color.WHITE);
navLink.setTextDecoration("none");
navLink.setPadding(8, 16);
navbar.addDescendant(navLink);

sheet.addRule(navbar);

// Style only direct children: .menu > li
CssRule menu = new CssRule(".menu");
CssRule menuItem = new CssRule("li");
menuItem.setListStyleType("none");
menuItem.setPadding(8);
menu.addChild(menuItem);

sheet.addRule(menu);

// Style siblings: .card + .card (gap between adjacent cards)
CssRule cardRule = new CssRule(".card");
cardRule.setBorderRadius(8);
CssRule nextCard = new CssRule(".card");
nextCard.setMarginTop(16);
cardRule.addAdjacentSibling(nextCard);

sheet.addRule(cardRule);

Every combinator method returns the rule for chaining, so you can compose as deeply as you need. The resulting CSS is generated automatically—you never have to think about >, +, or ~ again.

Pseudo-Classes and Pseudo-Elements

CSS pseudo-classes like :hover, :focus, and :first-child are another area where the syntax is easy to get wrong. With raw CSS, you write a separate selector for each state. With Oorian, you attach pseudo-class styles directly to the rule they belong to:

CssRule link = new CssRule("a");
link.setColor("#2563eb");
link.setTextDecoration("none");

// Pseudo-classes—no separate selectors needed
CssStyle hoverStyle = new CssStyle();
hoverStyle.setColor("#1d4ed8");
hoverStyle.setTextDecoration("underline");
link.setHoverStyle(hoverStyle);

CssStyle focusStyle = new CssStyle();
focusStyle.setOutline("2px solid #2563eb");
focusStyle.setOutlineOffset("2px");
link.setFocusStyle(focusStyle);

link.setVisitedStyle("color: #7c3aed;");
link.setActiveStyle("color: #dc2626;");

sheet.addRule(link);

This generates a { ... }, a:hover { ... }, a:focus { ... }, a:visited { ... }, and a:active { ... } all from a single rule object. Every pseudo-class method accepts either a CssStyle object or a plain CSS string—whichever is more convenient.

Oorian provides methods for 25 pseudo-classes covering interactive states, form validation, and structural selectors:

// Form validation styling
CssRule input = new CssRule("input");
input.setBorder(1, BorderStyle.SOLID, Color.LIGHT_GRAY);
input.setBorderRadius(4);
input.setPadding(8, 12);

input.setFocusStyle("border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);");
input.setInvalidStyle("border-color: #dc2626;");
input.setValidStyle("border-color: #16a34a;");
input.setDisabledStyle("background-color: #f3f4f6; cursor: not-allowed;");
input.setRequiredStyle("border-left: 3px solid #2563eb;");

sheet.addRule(input);

// Structural selectors
CssRule tableRow = new CssRule("tr");
tableRow.setNthChild(2, "background-color: #f8fafc;");  // Even rows
tableRow.setFirstChildStyle("font-weight: bold;");
tableRow.setLastChildStyle("border-bottom: none;");

sheet.addRule(tableRow);

Pseudo-elements work the same way. Style ::before, ::after, ::first-letter, ::first-line, ::marker, and ::selection directly on the rule:

// Required field indicator
CssRule label = new CssRule("label.required");
label.setFontWeight(FontWeight.WEIGHT_600);

CssStyle afterStyle = new CssStyle();
afterStyle.addStyleAttribute("content", "' *'");
afterStyle.setColor(Color.RED);
label.setAfterStyle(afterStyle);

sheet.addRule(label);

// Custom list markers
CssRule listItem = new CssRule("li");

CssStyle markerStyle = new CssStyle();
markerStyle.setColor(Color.CORNFLOWER_BLUE);
markerStyle.setFontWeight(FontWeight.WEIGHT_700);
listItem.setMarkerStyle(markerStyle);

// Custom text selection color
CssStyle selStyle = new CssStyle();
selStyle.setBackgroundColor(new Color("#2563eb"));
selStyle.setColor(Color.WHITE);
listItem.setSelectionStyle(selStyle);

sheet.addRule(listItem);

The key benefit is that all the styles for an element—its base styles, pseudo-classes, and pseudo-elements—live together on a single CssRule object instead of scattered across dozens of separate selectors in a CSS file.

Responsive Design with CssMediaQuery

Media queries are first-class citizens. Create a CssMediaQuery, add rules to it, and attach it to the stylesheet:

CssMediaQuery mobile = new CssMediaQuery("(max-width: 768px)");

CssRule mobileCard = new CssRule(".card-grid");
mobileCard.addStyleAttribute("grid-template-columns", "1fr");
mobile.addRule(mobileCard);

CssRule mobileNav = new CssRule(".nav-links");
mobileNav.setDisplay(Display.NONE);
mobile.addRule(mobileNav);

sheet.addMediaQuery(mobile);

Animations with KeyFrames

Define CSS animations entirely in Java:

KeyFrames fadeIn = new KeyFrames("fadeIn");
fadeIn.addRule("from", "opacity: 0; transform: translateY(10px);");
fadeIn.addRule("to", "opacity: 1; transform: translateY(0);");
sheet.addKeyFrames(fadeIn);

CssRule animated = new CssRule(".fade-in");
animated.setAnimation("fadeIn 0.3s ease-out");
sheet.addRule(animated);

Using the Stylesheet in a Page

Once built, pass the CssStyleSheet to a Style element:

@Override
protected void createHead(Head head)
{
    super.createHead(head);

    Style style = new Style(createStyles());
    head.addElement(style);
}

private CssStyleSheet createStyles()
{
    CssStyleSheet sheet = new CssStyleSheet();
    // ... add rules, media queries, keyframes ...
    return sheet;
}

Serving CSS as an External File with CssFile

For styles shared across multiple pages, extend CssFile to serve a dynamically generated stylesheet as a proper CSS file. The browser caches it just like a static file, but you build it in Java:

@Css("/css/theme.css")
public class ThemeStyles extends CssFile
{
    public ThemeStyles()
    {
        super("theme-styles");  // Cache key—generated once
    }

    @Override
    protected CssStyleSheet createStyleSheet()
    {
        CssStyleSheet css = new CssStyleSheet();

        CssRule body = new CssRule("body");
        body.setFontFamily("'Inter', sans-serif");
        body.setColor("#1f2937");
        body.setBackgroundColor("#f8fafc");
        css.addRule(body);

        CssRule primaryBtn = new CssRule(".btn-primary");
        primaryBtn.setBackgroundColor("#2563eb");
        primaryBtn.setColor(Color.WHITE);
        primaryBtn.setPadding(10, 24);
        primaryBtn.setBorderRadius(6);
        primaryBtn.setBorder("none");
        primaryBtn.setCursor(Cursor.POINTER);
        primaryBtn.setTransitionProperty("background-color");
        primaryBtn.setTransitionDuration(0.2f);
        css.addRule(primaryBtn);

        CssRule primaryBtnHover = new CssRule(".btn-primary:hover");
        primaryBtnHover.setBackgroundColor("#1d4ed8");
        css.addRule(primaryBtnHover);

        CssMediaQuery mobile = new CssMediaQuery("(max-width: 768px)");
        CssRule mobileBtn = new CssRule(".btn-primary");
        mobileBtn.setWidth(new Percent(100));
        mobile.addRule(mobileBtn);
        css.addMediaQuery(mobile);

        return css;
    }
}

Then link to it from any page:

head.addCssLink("/css/theme.css");

CssFile supports two modes. Use the named constructor (as above) for cached mode—the stylesheet is generated once and served from memory on subsequent requests. Use the no-arg constructor for dynamic mode, where the stylesheet is regenerated on every request, useful for user-specific themes or runtime configuration.

When to use dynamic CSS: Any time you would otherwise write CSS by hand. The type-safe API prevents typos, gives you IDE autocomplete, and makes refactoring trivial. Use CssFile for shared styles across pages. Use CssStyleSheet with a Style element for page-specific rules.

The Color Class

Oorian includes a full-featured Color class in the com.oorian.css.color package. It goes well beyond simple hex strings—you get color space conversions, manipulation, blending, and all 140 standard HTML color names as type-safe constants.

Named HTML Colors

All 140 standard HTML colors are available as static constants. No more guessing hex codes:

card.setBackgroundColor(Color.ALICE_BLUE);
heading.setColor(Color.DARK_SLATE_GRAY);
badge.setBackgroundColor(Color.CORAL);
border.setBorderColor(Color.CORNFLOWER_BLUE);

Oorian also provides a fine-grained grayscale palette (GRAY_01 through GRAY_34) for precise control over neutral tones.

Creating Colors

Create colors from CSS hex strings, RGB integers, HSB, CIE XYZ, or CIELAB color spaces:

// From CSS hex string
Color brand = new Color("#2563eb");

// From RGB components (0-255)
Color salmon = new Color(250, 128, 114);

// From a single hex integer
Color amber = new Color(0xFFBF00);

// From HSB (hue 0-360, saturation 0-100, brightness 0-100)
Color vibrant = new Color(new Hsb(210, 80, 90));

// Copy constructor
Color brandCopy = new Color(brand);

Color Manipulation

Generate shades (darker) and tints (lighter) of any color without manual math:

Color primary = new Color("#2563eb");

// Darker shade (50% toward black)
Color darkPrimary = primary.getShadeOf(50);

// Lighter tint (30% toward white)
Color lightPrimary = primary.getTintOf(30);

// Use intensity to pick readable text color
int intensity = primary.getIntensity();
String textColor = intensity > 179 ? "#1f2937" : "#ffffff";

The getIntensity() method calculates perceived brightness using the standard luminance formula. Values above 179 suggest dark text; values below suggest light text.

Color Blending

Blend two or more colors together using alpha compositing:

// Blend two colors with alpha values
Color blended = Color.blend(Color.BLUE, 0.6f, Color.RED, 0.4f);

// Blend a list of colors
List<Color> palette = List.of(Color.RED, Color.GREEN, Color.BLUE);
Color mixed = Color.blend(palette, 0.5f);

Color Space Conversions

Convert between RGB, HSB, CIE XYZ, and CIELAB color spaces for advanced color work:

Color teal = Color.TEAL;

// Get HSB components
Hsb hsb = teal.getHsb();
int hue = teal.getHue();             // 0-360
int saturation = teal.getSaturation(); // 0-100
int brightness = teal.getBrightness(); // 0-100

// Get individual RGB components
int red = teal.getRed();
int green = teal.getGreen();
int blue = teal.getBlue();

// Convert to different color spaces
Xyz xyz = Color.rgbToXyz(Color.cssToRgb(teal.getCssString()));
Clab lab = Color.xyzToClab(xyz);

Output Formats

Get the color in whatever format you need:

Color c = Color.DODGER_BLUE;
String css = c.getCssString();           // "#1E90FF"
String hex = c.getHexString();           // "1e90ff"
String rgba = c.getCssString(0.75f);     // "rgba(30, 144, 255, 0.750000)"
int rgb = c.getRgb();                    // integer RGB value

Palettes and Random Colors

Generate color palettes for charts, dashboards, and data visualizations:

// Standard palette of distinct colors
List<Color> chartColors = Color.getStandardColors();

// Grayscale palette with a specific number of shades
List<Color> grays = Color.getGrayScale(10);

// Random color from the standard palette
Color highlight = Color.getRandomColor();

CSS Units

When CSS values are computed dynamically, building strings like width + "px" or fontSize + "rem" gets tedious fast. Oorian's com.oorian.css.units package provides type-safe unit classes that handle the formatting for you. Every CSS property that accepts a dimension also accepts a Units object.

Class CSS Unit Example Output
Px px (pixels) new Px(16) → "16.0px"
Em em new Em(1.5f) → "1.5em"
Rem rem (root em) new Rem(2) → "2.0rem"
Percent % new Percent(50) → "50%"
Vh / Vw vh / vw (viewport) new Vh(100) → "100.0vh"
Vmin / Vmax vmin / vmax new Vmin(50) → "50.0vmin"
Pt pt (points) new Pt(12) → "12.0pt"
Ch ch (character width) new Ch(40) → "40.0ch"
Ex ex (x-height) new Ex(2) → "2.0ex"
Cm / Mm / In / Pc cm / mm / in / pc Print-oriented units

The real value shows up when sizes are computed at runtime:

// Without units—manual string concatenation
int columns = getColumnCount();
sidebar.setWidth((100 / columns) + "%");
sidebar.setMinWidth(sidebarMinPx + "px");
sidebar.setPadding(basePadding + "rem");

// With units—clean and type-safe
int columns = getColumnCount();
sidebar.setWidth(new Percent(100 / columns));
sidebar.setMinWidth(new Px(sidebarMinPx));
sidebar.setPadding(new Rem(basePadding));

Units work everywhere dimensions are accepted—on elements, in CssStyle objects, and in CssRule declarations:

// On elements
card.setWidth(new Percent(100));
card.setMaxWidth(new Px(600));
card.setPadding(new Rem(1.5f));
card.setBorderRadius(new Em(0.5f));

// In CssRule declarations
CssRule container = new CssRule(".container");
container.setMaxWidth(new Px(1200));
container.setMargin(new Px(0), new Px(0));  // auto centering still uses strings
container.setPadding(new Px(0), new Rem(1));

// Viewport-relative sizing
CssRule hero = new CssRule(".hero");
hero.setHeight(new Vh(100));
hero.setMinHeight(new Px(600));

// Character-based width for readable text
CssRule article = new CssRule("article");
article.setMaxWidth(new Ch(70));

All unit classes extend the abstract Units base class. Their toString() method produces the CSS string, so they integrate seamlessly with any method that formats values to CSS output.

Choosing the Right Approach

Approach Best For Key Class
Inline CSS Dynamic values, per-element styling, state-based styles CssStyle, StyledElement methods
Internal CSS Page-specific rules with selectors and pseudo-classes Style
External CSS Static files, third-party libraries, browser caching head.addCssLink()
Dynamic CSS Type-safe stylesheets, shared styles, responsive design, animations CssStyleSheet, CssRule, CssFile

Conclusion

Oorian covers the full spectrum of CSS styling—from simple inline properties to complete dynamically generated stylesheets—all in pure Java. The type-safe API means your IDE catches CSS errors at compile time, autocomplete guides you through available properties, and refactoring is as easy as renaming a method. No more hunting for typos in CSS string literals or wondering which stylesheet overrides which. Just clean, composable Java code that produces exactly the CSS you need.

Related Articles

Deep Dive

Oorian Add-Ons: Server-Side Building Blocks for Real Applications

February 17, 2026
Deep Dive

Oorian's Built-In JavaScript APIs: Control the Browser from Java

February 12, 2026
Deep Dive

LaunchPad: Self-Contained Deployment for Oorian Applications

February 5, 2026