Skip to content

Styling

Edgar Mesquita edited this page Feb 9, 2026 · 10 revisions

eQuantic.UI Styling Architecture

Core Philosophy

eQuantic.UI aims to provide a Flutter-inspired developer experience (DX) while leveraging the full power of the Modern Web platform.

The styling architecture is built on three pillars:

  1. Abstraction: Components define what to style, not how.
  2. Flexibility: Tailwind CSS is the "Happy Path", but not the only path.
  3. Performance: Compilation-time class generation, minimizing runtime CSS-in-JS overhead.

1. The Core Abstraction (eQuantic.UI.Core)

At the core, every visual component inherits from HtmlElement, exposing two standard properties:

public abstract class HtmlElement : Component {
    /// <summary>
    /// Raw CSS classes (space-separated).
    /// </summary>
    public string ClassName { get; set; }

    /// <summary>
    /// Inline styles for dynamic values (e.g., coordinates, colors from DB).
    /// </summary>
    public Dictionary<string, string> Style { get; set; }
}

This simple contract means eQuantic.UI does not enforce any CSS framework. It simply renders HTML attributes.

The Theme Concept

To support multiple frameworks, we propose an ITheme interface in the Code:

public interface ITheme {
    string PrimaryButton { get; }
    string Card { get; }
    string Input { get; }
}

2. Tailwind CSS: The Default Implementation

We recommend Tailwind CSS v4 as the standard implementation due to its utility-first nature, which aligns perfectly with component composition.

Integration Flow

  1. Compiler: Transpiles C# ClassName="p-4" directly to JS/HTML.
  2. Tailwind CLI: Scans the output folder (wwwroot/_equantic/**/*.js) for class names.
  3. Browser: Receives an optimized .css file.

Helper Package: eQuantic.UI.Tailwind (Proposed)

Instead of hardcoding strings, users can use a helper library:

using static eQuantic.UI.Tailwind.Utility;

new Button {
    // Type-safe(ish) helpers
    ClassName = Flex.Row + Gap(4) + Bg.Blue500 + Text.White
};

Note: Strings are still preferred for developers familiar with Tailwind semantics.

2.1 Installation & Setup Guide

To use Tailwind with eQuantic.UI in a .NET project:

1. Add Tailwind Package

Add the Tailwind integration package to your project:

<PackageReference Include="eQuantic.UI.Tailwind" Version="0.1.1" />

2. Configure CSS Input

Create wwwroot/css/styles.css:

@import "tailwindcss";

@theme {
  --font-family-sans: "Inter", "sans-serif";
  --color-primary: #3b82f6;
}

3. Enable Tailwind in Your Project

The eQuantic.UI.Tailwind package automatically configures MSBuild targets. Just add this to your .csproj:

<PropertyGroup>
  <TailwindInputFile>wwwroot/css/styles.css</TailwindInputFile>
  <TailwindOutputFile>wwwroot/css/app.css</TailwindOutputFile>
</PropertyGroup>

4. Reference in HTML

In your index.html (served by the backend):

<link href="/css/app.css" rel="stylesheet" />

Tip

Zero External Dependencies: The Tailwind CLI is bundled via Bun inside the NuGet packages. No Node.js, npm, or global installations required - just dotnet build.


3. Extensibility & Other Frameworks

Because ClassName is just a string, adapting to other frameworks is trivial. The user can create extension methods or a dedicated theme package.

Example: Bootstrap 5

A user or a community package (eQuantic.UI.Bootstrap) could provide:

public static class BootstrapTheme {
    public const string BtnPrimary = "btn btn-primary";
    public const string Card = "card p-3";
}

// Usage
new Button { ClassName = BootstrapTheme.BtnPrimary, Text = "Click Me" }

Example: Custom / Legacy CSS

For legacy projects, standard CSS classes work natively:

new Container { ClassName = "my-legacy-sidebar" }

4. Proposed Package Structure

To maintain separation of concerns, we suggest splitting the styling helpers:

Package Purpose
eQuantic.UI.Core Base definitions, HtmlElement, ITheme interface.
eQuantic.UI.Tailwind (Recommended) Helpers, predefined design system matching basic specifictaion.
eQuantic.UI.Bootstrap (Optional) Token mapping for Bootstrap classes.
eQuantic.UI.Material (Optional) Implementation of Material Design using CSS variables/classes.

5. Global Styling & Configuration

Application Entry Point

The specific theme implementation is configured at startup (conceptually), although mostly it's a compile-time decision of which CSS file to include in index.html.

In Program.cs or App.cs:

// Defines standard "semantic" tokens for the app
public static class AppTheme {
    public static string Primary => "bg-indigo-600 hover:bg-indigo-700 text-white"; // Tailwind impl
    // OR
    // public static string Primary => "btn btn-primary"; // Bootstrap impl
}

Component Composition

Developers are encouraged to create their own specialized components rather than repeating styles:

public class PrimaryButton : Button {
    public PrimaryButton() {
        ClassName = AppTheme.Primary + " px-4 py-2 rounded shadow";
    }
}

This brings the "Component-First" mindset of React/Flutter to the styling layer.


6. Component Robustness Features

eQuantic.UI components now include production-ready features comparable to industry-leading libraries like shadcn/ui.

6.1 Compound Components

Components support semantic composition patterns:

new Card {
    Variant = CardVariant.Elevated,
    Children = {
        new CardHeader {
            Children = {
                new CardTitle { Text = "Title" },
                new CardDescription { Text = "Description" }
            }
        },
        new CardBody { /* content */ },
        new CardFooter { /* actions */ }
    }
}

6.2 Granular Variants

Multiple visual variants for different use cases:

// Card variants
new Card { Variant = CardVariant.Outline }    // Border only
new Card { Variant = CardVariant.Elevated }   // Prominent shadow
new Card { Variant = CardVariant.Ghost }      // Invisible container

// Input variants
new TextInput { /* ... */ }  // Uses theme.Input.GetVariant(Variant.Success)

6.3 Loading States

Built-in loading indicators:

new Button {
    Text = "Submit",
    Loading = isSubmitting,  // Shows spinner, disables button
    OnClick = HandleSubmit
}

6.4 Error States & Validation

Comprehensive validation feedback:

new FormField {
    Label = "Email",
    Required = true,
    Error = validationError,
    HelperText = "We'll never share your email",
    Children = {
        new TextInput { Type = "email" }
    }
}

6.5 Input Groups

Combine inputs with addons:

new InputGroup {
    Children = {
        new InputAddon { Text = "https://" },
        new TextInput { Placeholder = "example.com" },
        new Button { Text = "Go" }
    }
}

See Components for complete documentation.


7. Dark Mode Support

eQuantic.UI includes native support for dark mode, fully integrated with Tailwind's dark: variant and the browser's system preference.

7.1 How It Works

The framework manages a .dark class on the <html> element. This state is:

  1. Initialized via a lightweight script in the <head> to prevent flash of unstyled content (FOUC).
  2. Persisted to localStorage.
  3. Synced with system preferences by default.

7.2 Component Support

To support dark mode in your components, simply use the dark: prefix in your ClassName:

new Container {
    ClassName = "bg-white dark:bg-zinc-950 border-zinc-200 dark:border-zinc-800"
}

7.3 Theme Initialization

The AddUI service automatically injects the necessary theme data and initialization scripts. If you are using a custom shell, ensure you have the __EQUANTIC_THEME_DATA__ placeholder or include the ThemeProvider scripts manually.


8. Dynamic Highlight Themes (Prism.js)

For components like CodeBlock, the framework supports dynamic asset switching. The standard CodeBlock component automatically swaps between prism.min.css (light) and prism-tomorrow.min.css (dark) based on the active theme, ensuring syntax highlighting is always readable.


Clone this wiki locally