# Contributing

Source: https://lumeo.nativ.sh/docs/contributing

# Contributing

Thank you for your interest in contributing to Lumeo. This guide walks through setting up the project, understanding the codebase structure, creating components, writing documentation, and running tests.

## Getting started

### Prerequisites

-   **.NET 10 SDK** — Download from [dotnet.microsoft.com](https://dotnet.microsoft.com/download).
-   **Node.js** — Required for Tailwind CSS processing. Any recent LTS version works.
-   **IDE** — Visual Studio 2022+, JetBrains Rider, or VS Code with the C# Dev Kit extension.

### Clone and build

git clone https://github.com/aspect-build/lumeo.git cd lumeo # Build the component library dotnet build src/Lumeo/Lumeo.csproj # Run the docs site dotnet run --project docs/Lumeo.Docs/Lumeo.Docs.csproj

The docs site will be available at `https://localhost:5001` (or the port shown in your console).

### Build and pack

\# Build the library dotnet build src/Lumeo/Lumeo.csproj # Create a NuGet package dotnet pack src/Lumeo/Lumeo.csproj

## Project structure

The repository is organized into three main areas:

Path

Description

src/Lumeo/

The component library. Published as the `Lumeo` NuGet package.

src/Lumeo/UI/

All components, organized by name. Each component lives in its own folder (e.g. `UI/Button/`, `UI/Dialog/`).

src/Lumeo/Services/

Shared services: `ToastService`, `OverlayService`, `ThemeService`, `KeyboardShortcutService`, `ComponentInteropService`.

src/Lumeo/wwwroot/

Static assets. CSS in `css/`, JavaScript in `js/`.

docs/Lumeo.Docs/

The documentation site (Blazor Server). Contains pages, layouts, and shared components.

tests/Lumeo.Tests/

Unit and integration tests using bUnit.

## Creating a component

Every component follows a consistent structure. Here is a step-by-step guide.

### 1\. Create the component folder

Create a new folder under `src/Lumeo/UI/` named after your component. For example, `src/Lumeo/UI/MyComponent/`.

### 2\. Follow the required conventions

Every `.razor` file must include these elements:

@namespace Lumeo <div class="@($"{BaseClasses} {Class}".Trim())" @attributes="AdditionalAttributes"> @ChildContent </div> @code { \[Parameter\] public RenderFragment? ChildContent { get; set; } \[Parameter\] public string? Class { get; set; } \[Parameter(CaptureUnmatchedValues = true)\] public Dictionary<string, object>? AdditionalAttributes { get; set; } private string BaseClasses => "your-tailwind-classes-here"; }

Key rules:

-   **@namespace Lumeo** must be the first line of every `.razor` file.
-   **Class parameter** — Always include `[Parameter] public string? Class { get; set; }` for consumer CSS overrides.
-   **AdditionalAttributes** — Always include `[Parameter(CaptureUnmatchedValues = true)]` and spread it on the root element with `@attributes`.
-   **CSS class combining** — Use the pattern `$"{BaseClasses} {Class}".Trim()` to merge base and consumer classes.

### 3\. Use CSS variables for all colors

Never use hardcoded hex or HSL values. Always reference CSS variables through Tailwind utility classes:

Do

Do not

bg-primary

bg-blue-500

text-foreground

text-gray-900

border-border

border-\[#e5e7eb\]

bg-muted

bg-\[hsl(210,40%,96%)\]

Do **not** use Tailwind `dark:` prefixes. Dark mode is handled entirely through CSS variable swaps in `lumeo.css`.

### 4\. Define enums inside the component

If your component needs variants, sizes, or other enumerated options, define the enum as a nested type inside the `@code` block:

@code { \[Parameter\] public AlertVariant Variant { get; set; } = AlertVariant.Default; public enum AlertVariant { Default, Destructive, Success, Warning } }

Consumers reference these as `Alert.AlertVariant.Success`.

### 5\. Use CascadingValue for state

When child components need to read parent state, use `CascadingValue` with a context record. Define the record as a nested type in the parent component. Use `IsFixed="false"` when the state can change.

### 6\. JS interop through ComponentInteropService

Never inject `IJSRuntime` directly in components. Instead, inject `ComponentInteropService` and call its methods:

@inject ComponentInteropService Interop @code { // Scroll locking for modals await Interop.LockScroll(); await Interop.UnlockScroll(); // Focus trapping for overlays await Interop.SetupFocusTrap(elementRef); await Interop.RemoveFocusTrap(elementRef); // Click-outside detection await Interop.RegisterClickOutside(elementRef, callback); }

### 7\. Icons

Use the `Blazicon` component from the `Blazicons.Lucide` package:

<Blazicon Svg="Lucide.ChevronDown" class="h-4 w-4" /> <Blazicon Svg="Lucide.X" class="h-4 w-4" /> <Blazicon Svg="Lucide.Check" class="h-4 w-4" />

## Adding documentation

Every component should have a corresponding documentation page in the docs site.

### 1\. Create the page file

Add a new `.razor` file under `docs/Lumeo.Docs/Pages/Components/`. Use this template:

@page "/components/my-component" @namespace Lumeo.Docs.Pages.Components <PageTitle>MyComponent — Lumeo</PageTitle> <Container MaxWidth="3xl" Padding="0" Center="false"> <Stack Gap="10"> <!-- Header with title and description --> <!-- Live demos --> <!-- When to use section --> <!-- API reference table --> </Stack> </Container>

### 2\. Required sections

-   **Header** — Component name as an H1, followed by a brief description.
-   **Live demos** — Interactive examples showing the component in action. Use bordered cards for each example.
-   **When to use** — A list of appropriate use cases for the component.
-   **API reference** — A table listing all parameters with their types, defaults, and descriptions.

### 3\. Add to the navigation

Open `docs/Lumeo.Docs/Layout/NavMenu.razor` and add a `<NavLink>` in the appropriate category section.

## Running tests

The test suite uses [bUnit](https://bunit.dev) for component testing.

### Run all tests

dotnet test

### Run a specific test

dotnet test --filter "FullyQualifiedName~ButtonTests"

### Test conventions

-   Place tests in `tests/Lumeo.Tests/`, mirroring the source folder structure.
-   Name test files `{ComponentName}Tests.cs`.
-   Use bUnit's `RenderComponent<T>` to render and assert component output.
-   Test default rendering, parameter variations, event callbacks, and accessibility attributes.

### Example test

using Bunit; using Xunit; public class ButtonTests : TestContext { \[Fact\] public void Button\_RendersDefaultVariant() { var cut = RenderComponent<Button>(parameters => parameters.AddChildContent("Click me")); cut.Find("button").MarkupMatches( "<button class='...'>Click me</button>"); } \[Fact\] public void Button\_FiresOnClick() { var clicked = false; var cut = RenderComponent<Button>(parameters => parameters .AddChildContent("Click") .Add(p => p.OnClick, EventCallback.Factory.Create(this, () => clicked = true))); cut.Find("button").Click(); Assert.True(clicked); } }

## Code style

Consistent code style keeps the codebase readable and maintainable. Here are the key conventions.

Area

Convention

CSS framework

Tailwind CSS v4. All utility classes applied inline.

Colors

CSS variables only (`bg-primary`, `text-foreground`, etc.). No hardcoded hex/HSL values.

Dark mode

Handled by CSS variable swaps in `lumeo.css`. Never use `dark:` Tailwind prefixes.

Namespace

All component files use `@namespace Lumeo`.

Two-way binding

`Property` + `PropertyChanged` EventCallback pairs.

JS interop

Always through `ComponentInteropService`. Never inject `IJSRuntime` directly.

Enums

Defined as nested types inside the component's `@code` block.

Context records

Defined as nested `public record` inside the parent component.

Overlay cleanup

Implement `IAsyncDisposable`. Handle `JSDisconnectedException` in cleanup.

Icons

Use `<Blazicon Svg="Lucide.X" />` from `Blazicons.Lucide`.

## Pull request guidelines

-   **One component per PR.** Keep pull requests focused. If your change affects multiple components, split them into separate PRs.
-   **Include documentation.** New components should come with a documentation page. Changes to existing components should update the relevant docs.
-   **Add tests.** Include bUnit tests that cover the default rendering, parameter variations, and key interactions.
-   **Build must pass.** Run `dotnet build src/Lumeo/Lumeo.csproj` and `dotnet test` before submitting.
-   **Descriptive commit messages.** Use clear, concise commit messages that describe the "why" behind the change.
