diff --git a/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/.openspec.yaml b/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/.openspec.yaml
new file mode 100644
index 000000000..8fe205551
--- /dev/null
+++ b/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-06-12
diff --git a/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/design.md b/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/design.md
new file mode 100644
index 000000000..e8beeb3bd
--- /dev/null
+++ b/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/design.md
@@ -0,0 +1,92 @@
+# Design — Refurbish integration sandbox Blazor app to Operon + Tailwind
+
+## Context
+
+`src/sanbox/integration/ix-integration-blazor` is a Blazor Server app that demonstrates `RenderableContentControl` against the `ix-integration-plc` twin. It is mid-migration:
+
+- `Pages/_Layout.cshtml:11` — Bootstrap CSS link is commented out; `css/tailwind/tailwind.css` (compiled Tailwind 4 output) is linked instead.
+- `Pages/_Layout.cshtml:31-32` — `ix-bootstrap.bundle.min.js` and jQuery are still loaded from `AXSharp.Presentation.Blazor.Controls` static assets.
+- `Shared/NavMenu.razor`, `Shared/TopRow.razor`, `Shared/MainLayout.razor` — still pure Bootstrap/Blazor-template markup (`navbar`, `collapse`, `nav-item`, `form-control`, `oi oi-*` open-iconic icons, `page`/`sidebar` CSS).
+- Pages use scattered Bootstrap classes: `btn btn-outline-primary` (StackedLayout, TabLayout, WrappedLayout - Copy), `container`/`row` (AutoRendering, RenderingExamples), `card` (Components/IxComponentView).
+- Tailwind build pipeline exists: `package.json` (@tailwindcss/cli), `tailwind.ps1` (`App.css` → `wwwroot/css/tailwind/tailwind.css`), `wwwroot/css/operon-variables.css` already copied in but not imported.
+- `Inxton.Operon` NuGet package already referenced in the csproj; the renderable-content templates in `AXSharp.Presentation.Blazor.Controls` already render through `Operon.Components.BaseInput`, so the auto-generated UI is Operon-styled — only the host shell and page chrome are not.
+
+The reference implementation is `src/AXSharp.blazor/tests/sandbox/IxBlazor.App`, which completed the same migration: `_Host.cshtml` loads Operon CSS from `/_content/Inxton.Operon/css/momentum.css`, `MainLayout`/`NavMenu` are Tailwind, `Theme.razor` + `wwwroot/js/theme.js` implement light/dark/system toggle via `data-theme` attribute, and `Operon.Icons.HeroIcon` is used for icons.
+
+## Goals / Non-Goals
+
+**Audience:** customer demo aimed at PLC programmers — people who will read the ST declarations and judge whether RenderableContentControl saves them UI work. Pages must pair the live control with the declaration that produced it.
+
+**Goals:**
+- Zero Bootstrap: no Bootstrap classes, no `ix-bootstrap.bundle.min.js`, no jQuery, no open-iconic.
+- App shell and every demo page styled with Tailwind utilities, preferring Operon components (`BaseInput`, `Tab`/`TabPage`, `HeroIcon`) and Operon CSS variables over hand-rolled styles.
+- Each page reads as a deliberate showcase of one `RenderableContentControl` capability: title, short explanation, live control, and the ST/code snippet in a styled panel.
+- Full capability coverage: add demo pages for template overrides (`PresentationTemplate`), the presentation-type pipeline (Base/Manual/Service), and control parameters (`HideLabel`, `Class`/`LayoutClass`/`LayoutChildrenClass`) — implemented host-side only.
+- Fully offline: no CDN or external network dependency; demo must run on the hardware rig without internet.
+- Light/dark theming via Operon variables with a visible toggle.
+- Compelling first impression: a redesigned Index/landing page that explains what RenderableContentControl does and links to the scenarios.
+
+**Non-Goals:**
+- No changes to `AXSharp.Presentation.Blazor.Controls`, the connector, code generators, or PLC twin projects (`ix-integration-plc`, `ix-integration-library`) — new scenarios are chosen so existing twin types suffice.
+- No work on `IxBlazor.App` or the `probes/ax-blazor-1` sandboxes; no sandbox convergence effort.
+- No automated visual-regression tooling.
+
+## Decisions
+
+1. **Load Operon CSS from the NuGet static assets, mirror IxBlazor.App's `_Host.cshtml`.**
+ `_Layout.cshtml` will link `/_content/Inxton.Operon/css/momentum.css` plus the locally compiled `css/tailwind/tailwind.css` for page-level utilities. Alternative considered: compiling everything into one local CSS — rejected because IxBlazor.App's split is the proven pattern and keeps Operon styling updatable via package bump.
+
+2. **Keep the existing local Tailwind pipeline; fix its input.**
+ `App.css` becomes the Tailwind source (`@import "tailwindcss"; @source "./"; @import "./operon-variables.css"; @custom-variant dark ...`), matching IxBlazor.App's `tailwind.css` source file. `tailwind.ps1` already targets `App.css` → `wwwroot/css/tailwind/tailwind.css`, so only the file content changes. The compiled output is committed, as elsewhere in the repo. Alternative: switch to MSBuild-integrated Tailwind — rejected as out of scope; repo convention is the ps1 + committed output.
+
+3. **Port `Theme.razor` + `theme.js` from IxBlazor.App rather than reinventing.**
+ Same `data-theme` attribute mechanism, same light/dark/system tri-state with `HeroIcon` sun/moon/computer-desktop icons. The Operon dark variant (`@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *))`) keys off this attribute.
+
+4. **App shell layout: top navbar with five story links.**
+ Replace the `page`/`sidebar` template structure with IxBlazor.App's pattern — a top header bar (brand, nav links, culture selector, theme toggle) and a centered `max-w-screen-xl` content container. Navigation is exactly five destinations in narrative order: Home, Rendering, Layouts, Customize, Integrate (see decision 10). No grouping taxonomy, no collapse toggle, no scrollable link row. Alternative: keep the sidebar and restyle it — rejected; the top-bar pattern matches the sibling sandbox and removes the collapse-toggle JS dependency.
+
+5. **Shared `ShowcaseSection` scaffolding component with static code snippets — Showdown and its CDN script removed.**
+ A small shared component (in `Shared/`) providing the repeated section chrome: section title, description slot, live-demo panel, and a collapsible "Code" panel. Chapter pages (decision 10) compose many of these. The code panel takes the ST snippet as a plain string (or `RenderFragment`) and renders it directly into a Tailwind-styled `
` block — no runtime markdown conversion, no JS interop, no `showdown.min.js` from cdnjs. Rationale: the demo must run offline on the hardware rig, and a CDN script is a hard online dependency; the snippets are static text known at build time, so runtime conversion adds nothing. Alternative considered: self-hosting Showdown — still dead weight for static content.
+
+6. **Icons: `Operon.Icons.HeroIcon` everywhere open-iconic was used.**
+ Nav links get meaningful icons (home, squares for layouts, clock for polling, language for localizations, etc.) instead of the uniform `oi-plus`.
+
+7. **Culture selector (`TopRow.razor`) becomes a Tailwind-styled `` in the header.**
+ Logic unchanged; only `form-control`/`ms-auto` classes replaced. Operon has no select component in use in the repo, so a plain styled select is acceptable.
+
+8. **`WrappedLayout - Copy.razor` is deleted with the other standalone pages.**
+ Its scenario (wrap layout + ST snippet) is salvaged as a section of `/layouts` per decision 10; no rename needed.
+
+9. **New capability demos are host-side only — no ST/twin changes.**
+ Coverage audit against the sibling sandbox found three gaps, all demonstrable with existing twin types:
+ - **Template override**: a custom Razor template component in the app selected via `RenderableContentControl`'s `PresentationTemplate` parameter, shown side-by-side with the default rendering (see decision 11 for the gauge). This demos the override capability without the `RenderTemplateOverride` ST attribute, which would require PLC twin regeneration (explicitly out of scope).
+ - **Presentation pipeline**: one twin (`Entry.Plc.test_example.ixcomponent_instance`, already used by RenderingExamples) rendered with Base/Manual/Service/Control presentations, with the fallback rules explained.
+ - **Control parameters**: `HideLabel`, `Class`, `LayoutClass`, `LayoutChildrenClass` demonstrated on one structure with visible before/after (absorbs ClassInjecting).
+ The PLC side already covers everything else (`weatherBase.st` has the full RenderIgnore matrix, `example.st` has all layout/group-layout combinations); demos expose, not extend, the twin.
+
+10. **Information architecture: four chapter pages telling the story declare → render → arrange → customize → integrate.**
+ Instead of restyling ~13 standalone pages, scenarios are consolidated into sections on four chapter pages, in narrative order:
+ - `/rendering` — Control vs Display, ReadOnce/ReadOnly, RenderIgnore matrix, presentation pipeline (absorbs AutoRendering + new presentations demo).
+ - `/layouts` — Stack, Wrap, Tabs, UniformGrid, GroupBox/Border, each with its ST declaration (absorbs Stacked/Wrapped/TabLayout + RenderingExamples layout sections).
+ - `/customize` — gauge template override, control parameters, localizations (absorbs ClassInjecting + Localizations + new override demo).
+ - `/integrate` — shadow properties, polling intervals, arrays, custom views from external libraries, measurement composed views (absorbs ShadowProperties + Polling + Arrays + RenderingExamples external views + Measurement).
+ Old routes and page files are deleted, including the `Test.razor` scratch page; nothing external links to a demo app, so no redirects. Rationale: a customer demo is walked through linearly — chapter pages mirror the pitch script, and five nav links beat a 13-item menu. Alternative: keep one-page-per-scenario with grouped nav — rejected per IA decision; sections give each capability context within the story.
+
+11. **Landing hero: "ST in → UI out"; gauge override as the customization money-shot.**
+ - `/` hero shows a static ST declaration of the `weather` class next to the live `RenderableContentControl` rendering of that same struct with live PLC data, headline to the effect of "this UI was declared, not written", followed by chapter cards linking the story.
+ - The custom override template is an inline-SVG gauge bound to `MeasurementExample.Measurement` (`measurement.st`): needle = `Acquired`, scale = `Min`..`Max`, color from the `Result` enum (None/Passed/Failed), styled with Tailwind/Operon variables. Chosen over the originally floated weather-temperature gauge because `weather` has no temperature member (adding one would violate the no-PLC-changes constraint) and `Measurement.Simulate()` mutates values cyclically, so the gauge moves live during the demo. No charting library — keeps the zero-external-dependency/offline guarantee and shows that a template is just a Razor component. Same twin rendered default (left) vs gauge (right) makes the override capability legible at a glance for a programmer audience.
+
+## Risks / Trade-offs
+
+- [Operon package version pin (`Inxton.Operon` alpha) may lack a component the design assumes] → All assumed components (`BaseInput`, `Tab`, `HeroIcon`) are already consumed by IxBlazor.App against the same centrally-managed version; no new component dependencies are introduced.
+- [Removing jQuery/ix-bootstrap bundle could break a template that silently depends on it] → The renderable templates were already migrated to Operon (no Bootstrap JS usage); verify by exercising every page after removal. IxBlazor.App runs without these scripts.
+- [Visual verification requires a running PLC/WebAPI connector; hardware rig has warm-up flakes] → Pages render their chrome without a live PLC (controls show empty/default values); shell and styling can be verified without hardware. Full data verification done against the rig when available.
+- [Committed compiled CSS can drift from source] → tasks include regenerating `tailwind.css` as the final step after all class changes.
+
+## Migration Plan
+
+Single PR on `sandbox-app-refurbishment`. No deployment concerns (demo app). Rollback = revert the PR.
+
+## Open Questions
+
+- None. Nav grouping resolved by decision 10 (five story links).
diff --git a/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/proposal.md b/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/proposal.md
new file mode 100644
index 000000000..873aa0070
--- /dev/null
+++ b/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/proposal.md
@@ -0,0 +1,35 @@
+# Refurbish integration sandbox Blazor app to Operon + Tailwind
+
+## Why
+
+The integration sandbox app (`src/sanbox/integration/ix-integration-blazor`) is a **customer-facing demo for PLC programmers** — the primary demonstration of `RenderableContentControl` capabilities against a real PLC — but it is stuck mid-migration: Bootstrap CSS is commented out while the layout, navigation, and pages still use Bootstrap markup (`navbar`, `btn`, `container/row`, `form-control`, open-iconic icons), and `ix-bootstrap.bundle.min.js` + jQuery are still loaded. The result is a broken, unstyled demo that misrepresents what AXSharp + Operon can do. The renderable-content templates in `AXSharp.Presentation.Blazor.Controls` already render through Operon components, so the host app is the only piece left behind. The app also under-sells the control: template overrides, the presentation-type pipeline (Base/Manual/Service), and host-side control parameters (`HideLabel`, `Class`/`LayoutClass`) have no dedicated demo pages.
+
+## What Changes
+
+- Remove all Bootstrap remnants from `ix-integration-blazor`: Bootstrap utility classes in `.razor` files, commented-out `bootstrap.min.css` link, `ix-bootstrap.bundle.min.js`, jQuery, and open-iconic icon classes (`oi oi-*`).
+- Rebuild the app shell (`MainLayout`, `NavMenu`, `TopRow`, `_Layout.cshtml`) with Tailwind 4 utilities and Operon components (e.g. `HeroIcon` for nav icons, Operon variables for theming), following the patterns already proven in `src/AXSharp.blazor/tests/sandbox/IxBlazor.App`.
+- Restructure the demo as a story told in four chapter pages — `/rendering` (declare & render), `/layouts` (arrange), `/customize` (customize), `/integrate` (integrate) — each composed of showcase sections (title, description, live control, ST snippet panel). All existing scenarios (AutoRendering, Measurement, Stacked/Wrapped/Tab layouts, ShadowProperties, RenderingExamples, Arrays, Polling, ClassInjecting, Localizations) are absorbed as sections; their standalone pages and routes are removed, along with the `Test.razor` scratch page.
+- Redesign the landing page as an "ST in → UI out" hero: a static ST declaration side by side with the live `RenderableContentControl` rendering of that same struct, plus links to the four chapters.
+- Add light/dark theme support using the Operon CSS variable system (`operon-variables.css` is already in `wwwroot`) with a theme toggle, mirroring `IxBlazor.App`'s `Theme.razor` + `theme.js` approach.
+- Add demo sections for capabilities the app does not yet show, implemented host-side only (no PLC/ST changes): template override via the `PresentationTemplate` parameter with a custom SVG gauge template for the `Measurement` twin (live needle from `Acquired`, scale from `Min`/`Max`, color from `Result`), the presentation-type pipeline (Base/Manual/Service with fallback explained, using the existing `ixcomponent` twin), and control parameters (`HideLabel`, `Class`, `LayoutClass`, `LayoutChildrenClass` — consolidating/extending ClassInjecting).
+- Replace runtime Showdown markdown conversion (CDN script + JS interop) with static pre-rendered code snippets, so the demo works fully offline on the hardware rig.
+- Keep the existing Tailwind build pipeline (`tailwind.ps1`, `package.json`, `App.css` → `wwwroot/css/tailwind/tailwind.css`) and extend `App.css` to import Operon variables; regenerate the compiled CSS.
+- Delete dead artifacts: `WrappedLayout - Copy.razor`, commented-out error UI in `_Layout.cshtml`.
+- No changes to `AXSharp.Presentation.Blazor.Controls` templates, the connector, or the PLC twin projects (`ix-integration-plc`, `ix-integration-library`) — this is a host-app refurbishment only.
+
+## Capabilities
+
+### New Capabilities
+
+- `integration-sandbox-showcase`: The integration sandbox Blazor app presents RenderableContentControl scenarios (auto-rendering, layouts, shadow presentation, polling, localization, template overrides, presentation pipeline, control parameters) in a Bootstrap-free, Tailwind + Operon UI with light/dark theming, runnable fully offline.
+
+### Modified Capabilities
+
+
+
+## Impact
+
+- **Affected code**: `src/sanbox/integration/ix-integration-blazor` only — `Pages/*.razor` (including new capability demo pages), `Pages/_Layout.cshtml`, `Shared/*.razor`, `Components/*.razor` (plus new custom-template components), `wwwroot/css/App.css`, compiled `wwwroot/css/tailwind/tailwind.css`, new `wwwroot/js/theme.js`.
+- **Dependencies**: no new NuGet packages — `Inxton.Operon` is already referenced. Tailwind 4 toolchain already present (`@tailwindcss/cli` in `package.json`).
+- **Removed**: jQuery, `ix-bootstrap.bundle.min.js`, and Showdown CDN script tags; Bootstrap/open-iconic class usage; runtime markdown conversion (replaced by static pre-rendered snippets — no external network dependency, demo runs offline on the rig).
+- **Risk**: low — demo app, not shipped as a package. Main verification is visual (run app, check each page in light/dark) plus `dotnet build` of the sandbox solution. Note from prior work: this app is unrunnable against hardware pre-Operon; the refurbishment is what makes it runnable/demonstrable again.
diff --git a/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/specs/integration-sandbox-showcase/spec.md b/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/specs/integration-sandbox-showcase/spec.md
new file mode 100644
index 000000000..156b206d1
--- /dev/null
+++ b/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/specs/integration-sandbox-showcase/spec.md
@@ -0,0 +1,91 @@
+# integration-sandbox-showcase
+
+## ADDED Requirements
+
+### Requirement: Bootstrap-free integration sandbox
+The integration sandbox app (`ix-integration-blazor`) SHALL contain no Bootstrap artifacts: no Bootstrap CSS references (active or commented), no Bootstrap utility classes in markup, no `ix-bootstrap.bundle.min.js`, no jQuery, and no open-iconic (`oi oi-*`) icon classes.
+
+#### Scenario: No Bootstrap classes in markup
+- **WHEN** the project's `.razor` and `.cshtml` files are searched for Bootstrap class patterns (`navbar`, `btn btn-`, `form-control`, `container-fluid`, `nav-item`, `oi oi-`, `collapse`)
+- **THEN** no matches are found
+
+#### Scenario: No Bootstrap or jQuery scripts loaded
+- **WHEN** the rendered page's script tags are inspected
+- **THEN** neither `ix-bootstrap.bundle.min.js` nor `jquery-3.6.0.min.js` is loaded
+
+### Requirement: Offline operation
+The app SHALL run without internet access: all stylesheets, scripts, and fonts SHALL be served from the app itself or referenced package static assets, with no CDN or external URLs.
+
+#### Scenario: App runs on an offline rig
+- **WHEN** the app is served on a machine with no internet connectivity and a user navigates all pages
+- **THEN** every page renders fully styled and functional, and the host page contains no external (CDN) resource references
+
+#### Scenario: Code snippets render without runtime conversion
+- **WHEN** a user opens a code panel on any demo page
+- **THEN** the ST snippet is rendered from static pre-rendered content without invoking any JavaScript markdown converter
+
+### Requirement: Tailwind + Operon app shell
+The app shell (layout, navigation, header) SHALL be styled with Tailwind utilities and Operon components, loading Operon CSS from the `Inxton.Operon` package static assets and a locally compiled Tailwind stylesheet whose source imports the Operon CSS variables.
+
+#### Scenario: Stylesheets loaded
+- **WHEN** the app's host page is rendered
+- **THEN** it links `/_content/Inxton.Operon/css/momentum.css` and the locally compiled `css/tailwind/tailwind.css`
+
+#### Scenario: Navigation uses Operon icons
+- **WHEN** the navigation is rendered
+- **THEN** each destination is reachable via a `NavLink` decorated with an `Operon.Icons.HeroIcon` (no open-iconic spans)
+
+### Requirement: Light and dark theming
+The app SHALL support light, dark, and system theme modes driven by the Operon CSS variable system via a `data-theme` attribute, with a user-visible toggle in the header, and the selection SHALL persist across page reloads.
+
+#### Scenario: User switches to dark mode
+- **WHEN** the user selects dark mode from the theme toggle
+- **THEN** the root element carries `data-theme="dark"` and pages, including rendered `RenderableContentControl` content, restyle to dark Operon colors
+
+#### Scenario: Theme persists
+- **WHEN** the user reloads the app after selecting a theme
+- **THEN** the previously selected theme mode is applied
+
+### Requirement: Story-structured navigation
+The app SHALL organize all demos into four chapter pages in narrative order — Rendering (declare & render), Layouts (arrange), Customize, Integrate — reachable from a five-link navigation (Home + four chapters). Each demo SHALL be a section within its chapter, presented with a consistent showcase structure: a section title, a short description of the demonstrated capability, the live rendered control in a styled panel, and (where an ST snippet applies) a toggleable, styled code panel.
+
+#### Scenario: Navigation shows the story
+- **WHEN** the user views the navigation
+- **THEN** it contains exactly Home, Rendering, Layouts, Customize, and Integrate in that order
+
+#### Scenario: Layout section rendered
+- **WHEN** a user opens the Layouts chapter
+- **THEN** each layout (Stack, Wrap, Tabs, UniformGrid, GroupBox/Border) appears as a section with a description, the live `RenderableContentControl` output, and a control revealing the ST declaration snippet in a styled code panel
+
+#### Scenario: All existing scenarios preserved as sections
+- **WHEN** the user walks the four chapters
+- **THEN** every pre-refurbishment demo remains reachable as a section: AutoRendering content (Rendering), Stacked/Wrapped/Tab layouts and rendering examples (Layouts), class injecting and localizations (Customize), Measurement, Shadow presentation, Arrays, and Polling (Integrate)
+
+### Requirement: Capability coverage sections
+The app SHALL include dedicated demo sections for RenderableContentControl capabilities currently undemonstrated, implemented without modifying the PLC twin projects: template override via the `PresentationTemplate` parameter (Customize chapter), the presentation-type pipeline (Base, Manual, Service, Control — Rendering chapter), and host-side control parameters (`HideLabel`, `Class`, `LayoutClass`, `LayoutChildrenClass` — Customize chapter).
+
+#### Scenario: Gauge template override demo
+- **WHEN** the user opens the template-override section of the Customize chapter
+- **THEN** the same `Measurement` twin structure is shown rendered with its default template and with a custom app-defined SVG gauge template (needle from `Acquired`, scale from `Min`/`Max`, color from `Result`) selected via `PresentationTemplate`, side by side
+
+#### Scenario: Presentation pipeline demo
+- **WHEN** the user opens the presentation-pipeline section of the Rendering chapter
+- **THEN** one twin object is rendered under Base, Manual, Service, and Control presentation types with an explanation of the fallback rules
+
+#### Scenario: Control parameters demo
+- **WHEN** the user opens the control-parameters section of the Customize chapter
+- **THEN** a structure is rendered with and without `HideLabel` and with injected `Class`/`LayoutClass`/`LayoutChildrenClass` values, with visible difference
+
+### Requirement: Landing page hero shows ST in, UI out
+The landing page SHALL present a hero that pairs a static ST declaration with the live `RenderableContentControl` rendering of that same structure, communicating that the UI is declared rather than hand-written, and SHALL link to the four chapter pages.
+
+#### Scenario: User opens the app root
+- **WHEN** the user navigates to `/`
+- **THEN** the hero shows the ST declaration of a twin structure side by side with its live rendered control, and chapter links to Rendering, Layouts, Customize, and Integrate
+
+### Requirement: Culture selection preserved
+The header SHALL retain the culture selector (en-US, sk-SK, es-ES) with unchanged switching behavior, styled with Tailwind instead of Bootstrap form classes.
+
+#### Scenario: User changes culture
+- **WHEN** the user selects a different culture in the header selector
+- **THEN** the app navigates to the culture controller and reloads with the selected culture applied
diff --git a/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/tasks.md b/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/tasks.md
new file mode 100644
index 000000000..c7adf1b8f
--- /dev/null
+++ b/openspec/changes/archive/2026-06-12-refurbish-sandbox-blazor-operon/tasks.md
@@ -0,0 +1,42 @@
+# Tasks — Refurbish integration sandbox Blazor app to Operon + Tailwind
+
+All paths relative to `src/sanbox/integration/ix-integration-blazor/` unless stated otherwise.
+
+## 1. CSS pipeline and host page
+
+- [x] 1.1 Rewrite `wwwroot/css/App.css` as the Tailwind 4 source: `@import "tailwindcss"`, `@source "./"`, `@import "./operon-variables.css"`, and the dark custom variant (`@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *))`) — mirror `src/AXSharp.blazor/tests/sandbox/IxBlazor.App/wwwroot/css/tailwind.css`; purge the old Blazor-template rules (`.page`/`.sidebar`/`.top-row`)
+- [x] 1.2 Update `Pages/_Layout.cshtml`: add `/_content/Inxton.Operon/css/momentum.css` link, keep `css/tailwind/tailwind.css`, delete the commented Bootstrap link and commented error-UI block, remove `ix-bootstrap.bundle.min.js`, `jquery-3.6.0.min.js`, the Showdown CDN script, and the `converter` inline script (keep only blazor.server.js — offline requirement)
+- [x] 1.3 Port theme manager: copy/adapt `IxBlazor.App/wwwroot/js/theme.js` into `wwwroot/js/theme.js`, reference it from `_Layout.cshtml`
+
+## 2. App shell
+
+- [x] 2.1 Rebuild `Shared/MainLayout.razor` with the IxBlazor.App pattern: top header bar + centered `max-w-screen-xl` Tailwind content container; drop the `page`/`sidebar` template structure
+- [x] 2.2 Rebuild `Shared/NavMenu.razor` as a Tailwind top-nav with exactly five links in story order — Home, Rendering, Layouts, Customize, Integrate — each with a meaningful `Operon.Icons.HeroIcon`; no `collapse` toggle code
+- [x] 2.3 Rebuild `Shared/TopRow.razor` (or fold into NavMenu): Tailwind-styled culture `` (en-US, sk-SK, es-ES), logic unchanged
+- [x] 2.4 Add `Shared/Theme.razor` theme toggle (light/dark/system, HeroIcon sun/moon/computer-desktop) wired to `theme.js`, placed in the header
+
+## 3. Showcase scaffolding
+
+- [x] 3.1 Create `Shared/ShowcaseSection.razor` component: section title, description slot, styled live-demo panel, optional toggleable code panel rendering a static ST snippet string into Tailwind-styled `pre/code` (no Showdown, no JS interop)
+- [x] 3.2 Create the SVG gauge template component (e.g. `Components/Templates/MeasurementGaugeView.razor`): inline SVG arc gauge for the `Measurement` twin — needle from `Acquired`, scale from `Min`/`Max`, color from `Result` enum — Tailwind/Operon-variable styling, resolvable via `PresentationTemplate`
+
+## 4. Landing page (`/`)
+
+- [x] 4.1 Rebuild `Pages/Index.razor` as the "ST in → UI out" hero: static ST declaration of the `weather` class beside the live `RenderableContentControl` of `Entry.Plc.weather`, declared-not-written headline, chapter cards linking Rendering / Layouts / Customize / Integrate
+
+## 5. Chapter pages (compose ShowcaseSection; salvage content + snippets from old pages)
+
+- [x] 5.1 `Pages/Rendering.razor` (`/rendering`): sections for Control vs Display (`weather`), ReadOnce/ReadOnly (`weather_readOnce`/`weather_readOnly`), RenderIgnore matrix (`weather_wrapped`), and presentation pipeline — Base/Manual/Service/Control on `test_example.ixcomponent_instance` with fallback rules explained
+- [x] 5.2 `Pages/Layouts.razor` (`/layouts`): sections for Stack, Wrap, Tabs, UniformGrid (`test_example.primitives_*`, `weather_*`) and GroupBox/Border (`test_example.test_groupbox`/`test_border`), each with its ST declaration snippet
+- [x] 5.3 `Pages/Customize.razor` (`/customize`): sections for gauge template override on a `Measurement` twin (default vs `PresentationTemplate` gauge side by side — live-moving via `Simulate()` — with the custom-template snippet shown), control parameters (`HideLabel`, `Class`/`LayoutClass`/`LayoutChildrenClass` before/after), and localizations (salvage `Localizations.razor` content)
+- [x] 5.4 `Pages/Integrate.razor` (`/integrate`): sections for shadow properties (salvage `ShadowProperties.razor` incl. sync buttons), polling intervals (salvage `Pollings/Polling.razor` interval comparison), arrays (salvage `Arrays.razor`), custom views from external libraries (salvage `RenderingExamples.razor` external-view part), and measurement composed views (salvage `Measurement.razor`)
+- [x] 5.5 Restyle salvaged components: `Components/IxComponentView.razor` (replace Bootstrap `card`), `Components/MeasurementServiceView.razor`
+- [x] 5.6 Delete superseded pages and routes: `AutoRendering.razor`, `StackedLayout.razor`, `WrappedLayout - Copy.razor`, `TabLayout.razor`, `RenderingExamples.razor`, `ShadowProperties.razor`, `Pollings/Polling.razor`, `Arrays.razor`, `ClassInjecting.razor`, `Localizations.razor`, `Measurement.razor`, `Test.razor`
+
+## 6. Verification
+
+- [x] 6.1 Regenerate compiled CSS: `npm install` (if needed) then run `tailwind.ps1` one-shot (without `--watch`) so `wwwroot/css/tailwind/tailwind.css` reflects all class changes; commit output
+- [x] 6.2 Grep gate per spec: no `navbar|btn btn-|form-control|container-fluid|nav-item|oi oi-|bootstrap|showdown|cdnjs|jquery` matches in project `.razor`/`.cshtml` files
+- [x] 6.3 `dotnet build` of `ix-integration-blazor.csproj` succeeds
+- [x] 6.4 Run the app, walk the story (Home → Rendering → Layouts → Customize → Integrate) in light and dark mode; verify every pre-refurbishment scenario appears as a section, theme persists across reload, culture switch still works (PLC data optional — chrome verification sufficient without rig)
+- [x] 6.5 Offline gate: with browser devtools network tab open, confirm no requests leave the app origin / `_content` package assets
diff --git a/openspec/specs/integration-sandbox-showcase/spec.md b/openspec/specs/integration-sandbox-showcase/spec.md
new file mode 100644
index 000000000..ada071246
--- /dev/null
+++ b/openspec/specs/integration-sandbox-showcase/spec.md
@@ -0,0 +1,95 @@
+# integration-sandbox-showcase
+
+## Purpose
+
+Present `RenderableContentControl` capabilities (auto-rendering, layouts, shadow presentation, polling, localization, template overrides, presentation pipeline, control parameters) to PLC programmers through the integration sandbox Blazor app (`ix-integration-blazor`) in a Bootstrap-free, Tailwind + Operon UI with light/dark theming, runnable fully offline against a real PLC.
+
+## Requirements
+
+### Requirement: Bootstrap-free integration sandbox
+The integration sandbox app (`ix-integration-blazor`) SHALL contain no Bootstrap artifacts: no Bootstrap CSS references (active or commented), no Bootstrap utility classes in markup, no `ix-bootstrap.bundle.min.js`, no jQuery, and no open-iconic (`oi oi-*`) icon classes.
+
+#### Scenario: No Bootstrap classes in markup
+- **WHEN** the project's `.razor` and `.cshtml` files are searched for Bootstrap class patterns (`navbar`, `btn btn-`, `form-control`, `container-fluid`, `nav-item`, `oi oi-`, `collapse`)
+- **THEN** no matches are found
+
+#### Scenario: No Bootstrap or jQuery scripts loaded
+- **WHEN** the rendered page's script tags are inspected
+- **THEN** neither `ix-bootstrap.bundle.min.js` nor `jquery-3.6.0.min.js` is loaded
+
+### Requirement: Offline operation
+The app SHALL run without internet access: all stylesheets, scripts, and fonts SHALL be served from the app itself or referenced package static assets, with no CDN or external URLs.
+
+#### Scenario: App runs on an offline rig
+- **WHEN** the app is served on a machine with no internet connectivity and a user navigates all pages
+- **THEN** every page renders fully styled and functional, and the host page contains no external (CDN) resource references
+
+#### Scenario: Code snippets render without runtime conversion
+- **WHEN** a user opens a code panel on any demo page
+- **THEN** the ST snippet is rendered from static pre-rendered content without invoking any JavaScript markdown converter
+
+### Requirement: Tailwind + Operon app shell
+The app shell (layout, navigation, header) SHALL be styled with Tailwind utilities and Operon components, loading Operon CSS from the `Inxton.Operon` package static assets and a locally compiled Tailwind stylesheet whose source imports the Operon CSS variables.
+
+#### Scenario: Stylesheets loaded
+- **WHEN** the app's host page is rendered
+- **THEN** it links `/_content/Inxton.Operon/css/momentum.css` and the locally compiled `css/tailwind/tailwind.css`
+
+#### Scenario: Navigation uses Operon icons
+- **WHEN** the navigation is rendered
+- **THEN** each destination is reachable via a `NavLink` decorated with an `Operon.Icons.HeroIcon` (no open-iconic spans)
+
+### Requirement: Light and dark theming
+The app SHALL support light, dark, and system theme modes driven by the Operon CSS variable system via a `data-theme` attribute, with a user-visible toggle in the header, and the selection SHALL persist across page reloads.
+
+#### Scenario: User switches to dark mode
+- **WHEN** the user selects dark mode from the theme toggle
+- **THEN** the root element carries `data-theme="dark"` and pages, including rendered `RenderableContentControl` content, restyle to dark Operon colors
+
+#### Scenario: Theme persists
+- **WHEN** the user reloads the app after selecting a theme
+- **THEN** the previously selected theme mode is applied
+
+### Requirement: Story-structured navigation
+The app SHALL organize all demos into four chapter pages in narrative order — Rendering (declare & render), Layouts (arrange), Customize, Integrate — reachable from a five-link navigation (Home + four chapters). Each demo SHALL be a section within its chapter, presented with a consistent showcase structure: a section title, a short description of the demonstrated capability, the live rendered control in a styled panel, and (where an ST snippet applies) a toggleable, styled code panel.
+
+#### Scenario: Navigation shows the story
+- **WHEN** the user views the navigation
+- **THEN** it contains exactly Home, Rendering, Layouts, Customize, and Integrate in that order
+
+#### Scenario: Layout section rendered
+- **WHEN** a user opens the Layouts chapter
+- **THEN** each layout (Stack, Wrap, Tabs, UniformGrid, GroupBox/Border) appears as a section with a description, the live `RenderableContentControl` output, and a control revealing the ST declaration snippet in a styled code panel
+
+#### Scenario: All existing scenarios preserved as sections
+- **WHEN** the user walks the four chapters
+- **THEN** every pre-refurbishment demo remains reachable as a section: AutoRendering content (Rendering), Stacked/Wrapped/Tab layouts and rendering examples (Layouts), class injecting and localizations (Customize), Measurement, Shadow presentation, Arrays, and Polling (Integrate)
+
+### Requirement: Capability coverage sections
+The app SHALL include dedicated demo sections for RenderableContentControl capabilities currently undemonstrated, implemented without modifying the PLC twin projects: template override via the `PresentationTemplate` parameter (Customize chapter), the presentation-type pipeline (Base, Manual, Service, Control — Rendering chapter), and host-side control parameters (`HideLabel`, `Class`, `LayoutClass`, `LayoutChildrenClass` — Customize chapter).
+
+#### Scenario: Gauge template override demo
+- **WHEN** the user opens the template-override section of the Customize chapter
+- **THEN** the same `Measurement` twin structure is shown rendered with its default template and with a custom app-defined SVG gauge template (needle from `Acquired`, scale from `Min`/`Max`, color from `Result`) selected via `PresentationTemplate`, side by side
+
+#### Scenario: Presentation pipeline demo
+- **WHEN** the user opens the presentation-pipeline section of the Rendering chapter
+- **THEN** one twin object is rendered under Base, Manual, Service, and Control presentation types with an explanation of the fallback rules
+
+#### Scenario: Control parameters demo
+- **WHEN** the user opens the control-parameters section of the Customize chapter
+- **THEN** a structure is rendered with and without `HideLabel` and with injected `Class`/`LayoutClass`/`LayoutChildrenClass` values, with visible difference
+
+### Requirement: Landing page hero shows ST in, UI out
+The landing page SHALL present a hero that pairs a static ST declaration with the live `RenderableContentControl` rendering of that same structure, communicating that the UI is declared rather than hand-written, and SHALL link to the four chapter pages.
+
+#### Scenario: User opens the app root
+- **WHEN** the user navigates to `/`
+- **THEN** the hero shows the ST declaration of a twin structure side by side with its live rendered control, and chapter links to Rendering, Layouts, Customize, and Integrate
+
+### Requirement: Culture selection preserved
+The header SHALL retain the culture selector (en-US, sk-SK, es-ES) with unchanged switching behavior, styled with Tailwind instead of Bootstrap form classes.
+
+#### Scenario: User changes culture
+- **WHEN** the user selects a different culture in the header selector
+- **THEN** the app navigates to the culture controller and reloads with the selected culture applied
diff --git a/src/sanbox/integration/ix-integration-blazor/Components/IxComponentView.razor b/src/sanbox/integration/ix-integration-blazor/Components/IxComponentView.razor
index 46328df5f..2f6f1bbe2 100644
--- a/src/sanbox/integration/ix-integration-blazor/Components/IxComponentView.razor
+++ b/src/sanbox/integration/ix-integration-blazor/Components/IxComponentView.razor
@@ -1,20 +1,24 @@
-@namespace AXSharp.Presentation.Blazor.Controls.Templates
+@namespace AXSharp.Presentation.Blazor.Controls.Templates
@inherits RenderableComplexComponentBase
-Should overwrite IxComponent from integration library
+
+
App-defined view (overrides the library view)
-
-
IxBool: @Component.my_bool.Cyclic
-
IxInt: @Component.my_int.Cyclic
-
IxString: @Component.my_string.Cyclic
-
+
+
IxBool @Component.my_bool.Cyclic
+
IxInt @Component.my_int.Cyclic
+
IxString @Component.my_string.Cyclic
+
+
+
+
+
-
-@code
+@code
{
public override void ConfigurePolling()
{
StartPolling(Component);
}
-}
\ No newline at end of file
+}
diff --git a/src/sanbox/integration/ix-integration-blazor/Components/MeasurementServiceView.razor b/src/sanbox/integration/ix-integration-blazor/Components/MeasurementServiceView.razor
index 3fd021716..d85d47a65 100644
--- a/src/sanbox/integration/ix-integration-blazor/Components/MeasurementServiceView.razor
+++ b/src/sanbox/integration/ix-integration-blazor/Components/MeasurementServiceView.razor
@@ -1,11 +1,15 @@
-@namespace MeasurementExample
+@namespace MeasurementExample
@inherits RenderableComplexComponentBase
-IxComponentServiceView
+
+
Measurement service view
-
IxBool serviceView: @Component.Min.Cyclic
-
IxInt serviceView: @Component.Acquired.Cyclic
-
IxString serviceView: @Component.Max.Cyclic
+
+
Minimum @Component.Min.Cyclic
+
Measured @Component.Acquired.Cyclic
+
Maximum @Component.Max.Cyclic
+
+
@code
{
diff --git a/src/sanbox/integration/ix-integration-blazor/Components/Templates/MeasurementGaugeView.razor b/src/sanbox/integration/ix-integration-blazor/Components/Templates/MeasurementGaugeView.razor
new file mode 100644
index 000000000..06907c02b
--- /dev/null
+++ b/src/sanbox/integration/ix-integration-blazor/Components/Templates/MeasurementGaugeView.razor
@@ -0,0 +1,73 @@
+@namespace ix_integration_blazor.Components.Templates
+@using System.Globalization
+@inherits RenderableComplexComponentBase
+
+
+
@Component?.AttributeName
+
+
+ @* track *@
+
+ @* value arc *@
+
+ @* needle *@
+
+
+
+
+
@Acquired.ToString("0.0")
+
+
+ @Min.ToString("0.0")
+ @Max.ToString("0.0")
+
+
+
+ @ResultText
+
+
+
+@code {
+ private float Min => Component?.Min.Cyclic ?? 0;
+ private float Max => Component?.Max.Cyclic ?? 0;
+ private float Acquired => Component?.Acquired.Cyclic ?? 0;
+
+ private double Ratio
+ {
+ get
+ {
+ var span = Max - Min;
+ if (span <= 0) return 0;
+ return Math.Clamp((Acquired - Min) / span, 0d, 1d);
+ }
+ }
+
+ private const double Radius = 80;
+ private static double ArcLength => Math.PI * Radius;
+
+ private double NeedleX => 100 + (Radius - 14) * Math.Cos(Math.PI * (1 - Ratio));
+ private double NeedleY => 100 - (Radius - 14) * Math.Sin(Math.PI * (1 - Ratio));
+
+ private MeasurementExample.Result Result => (MeasurementExample.Result)(Component?.Result.Cyclic ?? 0);
+
+ private string ResultColor => Result switch
+ {
+ MeasurementExample.Result.Passed => "var(--color-result-passed)",
+ MeasurementExample.Result.Failed => "var(--color-result-failed)",
+ _ => "var(--color-primary)"
+ };
+
+ private string ResultText => Result.ToString();
+
+ // SVG coordinates must not be culture-formatted (sk-SK uses decimal comma)
+ private static string Inv(double value) => value.ToString("0.##", CultureInfo.InvariantCulture);
+
+ public override void ConfigurePolling()
+ {
+ StartPolling(this.Component, PollingInterval);
+ }
+}
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/Arrays.razor b/src/sanbox/integration/ix-integration-blazor/Pages/Arrays.razor
deleted file mode 100644
index d8cd4372f..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/Arrays.razor
+++ /dev/null
@@ -1,4 +0,0 @@
-@page "/Arrays"
-
-
-
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/AutoRendering.razor b/src/sanbox/integration/ix-integration-blazor/Pages/AutoRendering.razor
deleted file mode 100644
index 9adb4dcb9..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/AutoRendering.razor
+++ /dev/null
@@ -1,70 +0,0 @@
-@page "/AutoRendering"
-
-
-Rendering
-
-Rendering
-
-Structure rendering
-
-
-
-
-
- Control - can edit
-
-
-
-
-
- Display - cannot edit
-
-
-
-
-
-
-
-This structure has [ReadOnce()] attribute
-
- It will be read only once at the in a life time to of the application.
- This can be set for items or structured that do not change during the run of the application.
- This feature aim at saving load on communication with the PLC.
-
-
-
-
-This structure has [ReadOnly()] attribute
-
- The structure values won't be written in the PLC.
-
-
-
-
---------------------------------
-
-Render ignore(s)
-
-This structures presentation type is Control attribute
-
- Members annotated with [RenderIgnore("Control")] must not be displayed.
-
-
-
-
-
-This structures presentation type is Display attribute
-
- Members annotated with [RenderIgnore("Display")] must not be displayed.
-
-
-
-@code {
-
-}
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/ClassInjecting.razor b/src/sanbox/integration/ix-integration-blazor/Pages/ClassInjecting.razor
deleted file mode 100644
index 668a2be59..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/ClassInjecting.razor
+++ /dev/null
@@ -1,23 +0,0 @@
-@page "/classinjecting"
-Css Class Injecting
-
-
-Wrap
-
-
-Stack
-
-
-
-Tabs
-
\ No newline at end of file
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/Customize.razor b/src/sanbox/integration/ix-integration-blazor/Pages/Customize.razor
new file mode 100644
index 000000000..cddf52a94
--- /dev/null
+++ b/src/sanbox/integration/ix-integration-blazor/Pages/Customize.razor
@@ -0,0 +1,109 @@
+@page "/customize"
+
+Customize — AX# RenderableContentControl
+
+Customize
+
+ Auto-generated does not mean one-size-fits-all. Swap the rendered view for your own Razor component,
+ inject CSS classes into the generated markup, or localize every label — all without touching the PLC code.
+
+
+
+
+ Both panels render the same live Measurement twin. The left uses the default generated
+ form; the right swaps in a custom SVG gauge via the PresentationTemplate parameter.
+ A template is just a Razor component — the needle follows Acquired, the scale follows
+ Min/Max, and the color follows the Result enum, live from the PLC.
+
+
+
+
+
+
PresentationTemplate — gauge
+
+
+
+
+
+
+
+
+ Host-side parameters tune the generated markup per instance: HideLabel drops the labels,
+ Class wraps the whole control, and LayoutClass/LayoutChildrenClass
+ inject classes into the generated layout and each of its children.
+
+
+
+
+
Class + LayoutClass + LayoutChildrenClass injected
+
+
+
+
+
+
+
+ Labels come from the PLC string resources and follow the request culture — switch the language in the
+ header (en-US, sk-SK, es-ES) and the generated UI re-renders localized, including this structure.
+
+
+
+
+
+
+@code {
+ private const string SnippetGauge = @"
+@namespace ix_integration_blazor.Components.Templates
+@inherits RenderableComplexComponentBase
+
+
+
+
+
+@code {
+ public override void ConfigurePolling()
+ => StartPolling(this.Component, PollingInterval);
+}
+
+// usage:
+ ";
+
+ private const string SnippetParameters = @"
+
+
+ ";
+}
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/Error.cshtml b/src/sanbox/integration/ix-integration-blazor/Pages/Error.cshtml
index 88d32066b..b928925d1 100644
--- a/src/sanbox/integration/ix-integration-blazor/Pages/Error.cshtml
+++ b/src/sanbox/integration/ix-integration-blazor/Pages/Error.cshtml
@@ -1,4 +1,4 @@
-@page
+@page
@model ix_integration_blazor.Pages.ErrorModel
@@ -8,34 +8,31 @@
Error
-
-
+
-
-
-
-
Error.
-
An error occurred while processing your request.
+
+
+
Error.
+
An error occurred while processing your request.
- @if (Model.ShowRequestId)
- {
-
- Request ID: @Model.RequestId
-
- }
-
-
Development Mode
-
- Swapping to the Development environment displays detailed information about the error that occurred.
-
-
- The Development environment shouldn't be enabled for deployed applications.
- It can result in displaying sensitive information from exceptions to end users.
- For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
- and restarting the app.
+ @if (Model.ShowRequestId)
+ {
+
+ Request ID: @Model.RequestId
-
+ }
+
+
Development Mode
+
+ Swapping to the Development environment displays detailed information about the error that occurred.
+
+
+ The Development environment shouldn't be enabled for deployed applications.
+ It can result in displaying sensitive information from exceptions to end users.
+ For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
+ and restarting the app.
+
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/Index.razor b/src/sanbox/integration/ix-integration-blazor/Pages/Index.razor
index 29737cef2..ae98644ff 100644
--- a/src/sanbox/integration/ix-integration-blazor/Pages/Index.razor
+++ b/src/sanbox/integration/ix-integration-blazor/Pages/Index.razor
@@ -1,10 +1,79 @@
-@page "/"
+@page "/"
-
Index
+
AX# RenderableContentControl
+
+
+
+ This UI was declared , not written.
+
+
+ RenderableContentControl
+ turns your structured-text declarations into a live, data-bound user interface.
+ The form on the right is generated at runtime from the PLC class on the left — no UI code involved.
+
+
+
+
+
+ ST in — weather.st
+
+
{#ix[Container(Layout.Tabs)]}
+CLASS weather
+ VAR PUBLIC
+ GeoLocation : GeoLocation;
+ {#ix[Container(Layout.Wrap)]}
+ Temperature : REAL;
+ Humidity : REAL;
+ Location : STRING;
+ ChillFactor : REAL;
+ Feeling : Feeling;
+ END_VAR
+END_CLASS
+
-
- This application has direct references to the AX# projects, aim at testing, poc, and demostration of AXSharp.
- It won't work outside this solution.
-
\ No newline at end of file
+
+
+ UI out — live from the PLC
+
+
+
+
+
+
+
+
+
Explore the capabilities
+
+
+
+ Rendering
+
+ Control and Display presentations, read-only and read-once members, fine-grained render filtering.
+
+
+
+
+ Layouts
+
+ Arrange generated UI with stack, wrap, tab, and grid containers declared as attributes in ST.
+
+
+
+
+ Customize
+
+ Swap in your own templates, inject CSS classes, and localize labels without touching the PLC code.
+
+
+
+
+ Integrate
+
+ Shadow buffers, polling control, arrays, and reusable component views from external libraries.
+
+
+
+
+
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/Integrate.razor b/src/sanbox/integration/ix-integration-blazor/Pages/Integrate.razor
new file mode 100644
index 000000000..5650b77d5
--- /dev/null
+++ b/src/sanbox/integration/ix-integration-blazor/Pages/Integrate.razor
@@ -0,0 +1,95 @@
+@page "/integrate"
+@using AXSharp.Connector
+
+
Integrate — AX# RenderableContentControl
+
+
Integrate
+
+ Production scenarios: edit safely against a shadow buffer, tune communication per control,
+ render whole arrays, and reuse component views shipped in external libraries.
+
+
+
+
+ ShadowControl edits the twin's in-memory shadow buffer instead of writing straight to the
+ PLC — collect changes first, commit when ready. The values below live only in the application until
+ synchronized.
+
+
+
+
+
+
+
+
+ Each control decides how often it reads from the PLC via PollingInterval (milliseconds).
+ The three structures below poll the same kind of data at 500, 100, and 50 ms — watch the
+ update cadence differ.
+
+
+
+
+
Single primitive at 100 ms
+
+
+
+
+
+
+
+ Arrays of structures render without any extra declaration — every element gets the full
+ layout-aware treatment.
+
+
+
+
+
+
+
+
+ A class library can ship Razor views for its twin types; the locator picks them up from any assembly
+ marked [RenderableBlazorAssembly]. The same ixcomponent type from three ST
+ namespaces resolves to three different library views — and the application can override any of them
+ by declaring a view with the same name.
+
+
+
+
+
+
+
+
+
+
+
+
+ A structure of structures — four Measurement instances, each with its own declared
+ layout — renders as one coherent view from a single RenderableContentControl.
+
+
+
+
+
+
+@code {
+ private const string SnippetPolling = @"
+
+
+
";
+}
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/Layouts.razor b/src/sanbox/integration/ix-integration-blazor/Pages/Layouts.razor
new file mode 100644
index 000000000..f22066044
--- /dev/null
+++ b/src/sanbox/integration/ix-integration-blazor/Pages/Layouts.razor
@@ -0,0 +1,125 @@
+@page "/layouts"
+
+
Layouts — AX# RenderableContentControl
+
+
Arrange
+
+ Layout is declared where the data is declared. A [Container(Layout.*)] attribute on a class
+ or member arranges its children — stacked, wrapped, tabbed, or in a uniform grid — and
+ [Group(GroupLayout.*)] wraps them in a titled group box or border. No markup required.
+
+
+
+
+ Layout.Stack places members vertically, one per row.
+
+
+
+
+
+
+
+
+ Layout.Wrap flows members horizontally and wraps to the next line as space runs out.
+
+
+
+
+
+
+
+
+ Layout.Tabs renders each member on its own tab page.
+
+
+
+
+
+
+
+
+ Layout.UniformGrid distributes members into evenly sized grid cells.
+
+
+
+
+
+
+
+
+ [Group(GroupLayout.GroupBox)] frames a structure with a titled box;
+ [Group(GroupLayout.Border)] draws a plain border. Both combine freely with any container layout.
+
+
+
+
+
+
+@code {
+ private const string SnippetStack = @"
+NAMESPACE Layouts.Stacked
+ {#ix-attr:[Container(Layout.Stack)]}
+ {#ix-attr:[Group(GroupLayout.GroupBox)]}
+ CLASS PUBLIC weather
+ VAR PUBLIC
+ Latitude : REAL;
+ Longitude : REAL;
+ Altitude : REAL;
+ Description : STRING;
+ LongDescription : STRING;
+ END_VAR
+ END_CLASS
+END_NAMESPACE";
+
+ private const string SnippetWrap = @"
+NAMESPACE Layouts.Wrapped
+ {#ix-attr:[Container(Layout.Wrap)]}
+ {#ix-attr:[Group(GroupLayout.GroupBox)]}
+ CLASS PUBLIC weather
+ VAR PUBLIC
+ Latitude : REAL;
+ Longitude : REAL;
+ Altitude : REAL;
+ Description : STRING;
+ LongDescription : STRING;
+ END_VAR
+ END_CLASS
+END_NAMESPACE";
+
+ private const string SnippetTabs = @"
+NAMESPACE Layouts.Tabbed
+ {#ix-attr:[Container(Layout.Tabs)]}
+ {#ix-attr:[Group(GroupLayout.GroupBox)]}
+ CLASS PUBLIC weather
+ VAR PUBLIC
+ Latitude : REAL;
+ Longitude : REAL;
+ Altitude : REAL;
+ Description : STRING;
+ LongDescription : STRING;
+ END_VAR
+ END_CLASS
+END_NAMESPACE";
+
+ private const string SnippetUniform = @"
+{#ix-attr:[Container(Layout.UniformGrid)]}
+primitives_uniform: test_primitive;";
+
+ private const string SnippetGroups = @"
+{#ix-attr:[Container(Layout.Stack)]}
+{#ix-attr:[Group(GroupLayout.GroupBox)]}
+test_groupbox: test_primitive;
+
+{#ix-attr:[Container(Layout.Stack)]}
+{#ix-attr:[Group(GroupLayout.Border)]}
+test_border: test_primitive;";
+}
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/Localizations.razor b/src/sanbox/integration/ix-integration-blazor/Pages/Localizations.razor
deleted file mode 100644
index d0e04bc1c..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/Localizations.razor
+++ /dev/null
@@ -1,6 +0,0 @@
-@page "/localizations"
-
-
Localizations
-
-
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/Measurement.razor b/src/sanbox/integration/ix-integration-blazor/Pages/Measurement.razor
deleted file mode 100644
index 28fa873d7..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/Measurement.razor
+++ /dev/null
@@ -1,70 +0,0 @@
-@page "/Measurement"
-
-
Rendering
-
-
-
-
-
-
-
-
-
- Control - can edit
-
-
-
-
-
- Display - cannot edit
-
-
-
-
-
-
-
-
-
-
- Control - can edit
-
-
-
-
-
- Display - cannot edit
-
-
-
-
-
-
-
-
-
-
- Control - can edit
-
-
-
-
-
- Display - cannot edit
-
-
-
-
-
-
-@code {
-
-}
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/Pollings/Polling.cs b/src/sanbox/integration/ix-integration-blazor/Pages/Pollings/Polling.cs
deleted file mode 100644
index f7878bed7..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/Pollings/Polling.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using ix_integration_plc;
-using AXSharp.Connector;
-
-namespace ix_integration_blazor.Pages.Pollings
-{
- public partial class Polling : IDisposable
- {
- public void Dispose()
- {
-
- }
- }
-}
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/Pollings/Polling.razor b/src/sanbox/integration/ix-integration-blazor/Pages/Pollings/Polling.razor
deleted file mode 100644
index 6b630bee2..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/Pollings/Polling.razor
+++ /dev/null
@@ -1,35 +0,0 @@
-@page "/pollings/polling"
-@using ix_integration_plc
-@using AXSharp.Connector;
-
-
-
-
-
Polling
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@code
-{
-
-
- protected override async Task OnInitializedAsync()
- {
- Entry.Plc.Connector.SubscriptionMode = ReadSubscriptionMode.Polling;
- }
-
-
-
-
-}
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/Rendering.razor b/src/sanbox/integration/ix-integration-blazor/Pages/Rendering.razor
new file mode 100644
index 000000000..4da4f3137
--- /dev/null
+++ b/src/sanbox/integration/ix-integration-blazor/Pages/Rendering.razor
@@ -0,0 +1,120 @@
+@page "/rendering"
+
+
Rendering — AX# RenderableContentControl
+
+
Declare & render
+
+ Every UI on this page is generated at runtime from a PLC class declaration.
+ The presentation mode decides whether the user can edit values, and attributes in the
+ ST code control what gets rendered at all.
+
+
+
+
+ The same weather structure rendered twice: Control generates editable
+ inputs that write back to the PLC, Display generates a read-only view.
+
+
+
+
+
+
Display — read only
+
+
+
+
+
+
+
+
+ [ReadOnce()] reads a member a single time per application lifetime — ideal for values
+ that never change, saving communication load. [ReadOnly()] renders an input that never
+ writes back to the PLC.
+
+
+
+
+
Structure with [ReadOnce()] member
+
+
+
+
Structure with [ReadOnly()] member
+
+
+
+
+
+
+
+
+ [RenderIgnore()] excludes a member from rendering — entirely, or only for the
+ presentation types you name. The structure below declares one member per variant; compare which
+ ones survive in Control vs Display.
+
+
+
+
+
+
+
+
+ The renderer locates a view component by convention: <type name><presentation>View.
+ A presentation like Manual-Service is a fallback pipeline — each name is tried in order
+ until a matching view is found. ixcomponent ships a default (Base) view and a
+ Service view; no Manual view exists, so the pipeline below falls through to
+ Service.
+
+
+
+
+
Base — default view
+
+
+
+
+
Manual-Service — falls back to Service
+
+
+
+
+
+
+@code {
+ private const string SnippetReadAttributes = @"
+{#ix-attr:[ReadOnly()]}
+LongDescription : STRING;
+
+{#ix-attr:[ReadOnce()]}
+StartCounter : INT;";
+
+ private const string SnippetRenderIgnore = @"
+{#ix-attr:[RenderIgnore()]}
+RenderIgnoreAllToghether : STRING;
+
+{#ix-attr:[RenderIgnore(""Control"")]}
+RenderIgnoreWhenControl : STRING;
+
+{#ix-attr:[RenderIgnore(""Display"")]}
+RenderIgnoreWhenDisplay : STRING;
+
+{#ix-attr:[RenderIgnore(""Control"", ""ShadowControl"")]}
+RenderIgnoreWhenControlAndShadow : STRING;
+
+{#ix-attr:[RenderIgnore(""Display"", ""ShadowDisplay"")]}
+RenderIgnoreWhenDisplayAndShadow : STRING;";
+}
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/RenderingExamples.razor b/src/sanbox/integration/ix-integration-blazor/Pages/RenderingExamples.razor
deleted file mode 100644
index 784f6cf2f..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/RenderingExamples.razor
+++ /dev/null
@@ -1,40 +0,0 @@
-@page "/examples"
-
RenderingExamples
-
-
Custom views from external libraries
-
-
-
-
-
-
Grouplayouts
-
GroupBox
-
-
Border
-
-
-
Layouts
-
Stack
-
-
Wrap
-
-
UniformGrid
-
-
Tab
-
-
-
-
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/ShadowProperties.razor b/src/sanbox/integration/ix-integration-blazor/Pages/ShadowProperties.razor
deleted file mode 100644
index 7850517b0..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/ShadowProperties.razor
+++ /dev/null
@@ -1,8 +0,0 @@
-@page "/ShadowProperties"
-
-
-
-
-
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/StackedLayout.razor b/src/sanbox/integration/ix-integration-blazor/Pages/StackedLayout.razor
deleted file mode 100644
index 60fcadc13..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/StackedLayout.razor
+++ /dev/null
@@ -1,51 +0,0 @@
-@page "/StackedLayout"
-@using ix_integration_plc
-@inject IJSRuntime JS
-
-
-
-
Rendering
-
-
StackLayout
-
-
- @text
-
-
-
- Show code
-
-
-
-
-
-
-
-
-
-@code {
-
- private MarkupString text;
- private string code_example =
- @"
- NAMESPACE Layouts.Stacked
- {#ix-attr:[Container(Layout.Stack)]}
- {#ix-attr:[Group(GroupLayout.GroupBox)]}
- CLASS PUBLIC weather
- VAR PUBLIC
- Latitude : REAL;
- Longitude : REAL;
- Altitude : REAL;
- Description : STRING;
- LongDescription : STRING;
- END_VAR
- END_CLASS
- END_NAMESPACE
- ";
-
- private async Task ShowCode()
- {
- text = new(await JS.InvokeAsync
("converter.makeHtml", code_example));
- }
-}
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/TabLayout.razor b/src/sanbox/integration/ix-integration-blazor/Pages/TabLayout.razor
deleted file mode 100644
index 93c7f66d6..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/TabLayout.razor
+++ /dev/null
@@ -1,51 +0,0 @@
-@page "/TabLayout"
-@using ix_integration_plc
-@inject IJSRuntime JS
-
-
-
-Rendering
-
-StackLayout
-
-
- @text
-
-
-
- Show code
-
-
-
-
-
-
-
-
-
-@code {
-
- private MarkupString text;
- private string code_example =
- @"
- NAMESPACE Layouts.Tabbed
- {#ix-attr:[Container(Layout.Tabs)]}
- {#ix-attr:[Group(GroupLayout.GroupBox)]}
- CLASS PUBLIC weather
- VAR PUBLIC
- Latitude : REAL;
- Longitude : REAL;
- Altitude : REAL;
- Description : STRING;
- LongDescription : STRING;
- END_VAR
- END_CLASS
- END_NAMESPACE
- ";
-
- private async Task ShowCode()
- {
- text = new(await JS.InvokeAsync("converter.makeHtml", code_example));
- }
-}
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/Test.razor b/src/sanbox/integration/ix-integration-blazor/Pages/Test.razor
deleted file mode 100644
index 82615b953..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/Test.razor
+++ /dev/null
@@ -1,10 +0,0 @@
-@page "/Test"
-
-Rendering
-
-
-
-
-
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/WrappedLayout - Copy.razor b/src/sanbox/integration/ix-integration-blazor/Pages/WrappedLayout - Copy.razor
deleted file mode 100644
index c4e102875..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Pages/WrappedLayout - Copy.razor
+++ /dev/null
@@ -1,51 +0,0 @@
-@page "/WrappedLayout"
-@using ix_integration_plc
-@inject IJSRuntime JS
-
-
-
-Rendering
-
-StackLayout
-
-
- @text
-
-
-
- Show code
-
-
-
-
-
-
-
-
-
-@code {
-
- private MarkupString text;
- private string code_example =
- @"
- NAMESPACE Layouts.Wrapped
- {#ix-attr:[Container(Layout.Wrap)]}
- {#ix-attr:[Group(GroupLayout.GroupBox)]}
- CLASS PUBLIC weather
- VAR PUBLIC
- Latitude : REAL;
- Longitude : REAL;
- Altitude : REAL;
- Description : STRING;
- LongDescription : STRING;
- END_VAR
- END_CLASS
- END_NAMESPACE
- ";
-
- private async Task ShowCode()
- {
- text = new(await JS.InvokeAsync("converter.makeHtml", code_example));
- }
-}
diff --git a/src/sanbox/integration/ix-integration-blazor/Pages/_Layout.cshtml b/src/sanbox/integration/ix-integration-blazor/Pages/_Layout.cshtml
index fe509f3b6..8e2f9c6c3 100644
--- a/src/sanbox/integration/ix-integration-blazor/Pages/_Layout.cshtml
+++ b/src/sanbox/integration/ix-integration-blazor/Pages/_Layout.cshtml
@@ -1,4 +1,4 @@
-@using Microsoft.AspNetCore.Components.Web
+@using Microsoft.AspNetCore.Components.Web
@namespace ix_integration_blazor.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -8,7 +8,8 @@
- @* *@
+
+
@@ -16,23 +17,9 @@
@RenderBody()
- @* *@
- @*
*@
- @* An error has occurred. This application may no longer respond until reloaded. *@
- @* *@
- @*
*@
- @* An unhandled exception has occurred. See browser dev tools for details. *@
- @* *@
- @*
Reload *@
- @*
🗙 *@
- @*
*@
-
-
-
-
-
+
+
+
diff --git a/src/sanbox/integration/ix-integration-blazor/Program.cs b/src/sanbox/integration/ix-integration-blazor/Program.cs
index 21fd45d23..529a27ac1 100644
--- a/src/sanbox/integration/ix-integration-blazor/Program.cs
+++ b/src/sanbox/integration/ix-integration-blazor/Program.cs
@@ -33,6 +33,7 @@ public static void Main(string[] args)
builder.Services.AddSingleton();
builder.Services.AddIxBlazorServices();
builder.Services.AddLocalization();
+ builder.Services.AddScoped();
var app = builder.Build();
diff --git a/src/sanbox/integration/ix-integration-blazor/Shared/TopRow.razor b/src/sanbox/integration/ix-integration-blazor/Shared/CultureSelect.razor
similarity index 68%
rename from src/sanbox/integration/ix-integration-blazor/Shared/TopRow.razor
rename to src/sanbox/integration/ix-integration-blazor/Shared/CultureSelect.razor
index 93102c80d..8693b2819 100644
--- a/src/sanbox/integration/ix-integration-blazor/Shared/TopRow.razor
+++ b/src/sanbox/integration/ix-integration-blazor/Shared/CultureSelect.razor
@@ -1,19 +1,14 @@
-@using System.Globalization
+@using System.Globalization
@inject NavigationManager NavigationManager
-
-
-
-
- @foreach (var culture in supportedCultures)
- {
- @culture.NativeName
- }
-
-
-
-
-
+
+ @foreach (var culture in supportedCultures)
+ {
+ @culture.NativeName
+ }
+
@code {
private CultureInfo[] supportedCultures = new[]
@@ -41,4 +36,4 @@
{
Culture = CultureInfo.CurrentCulture;
}
-}
\ No newline at end of file
+}
diff --git a/src/sanbox/integration/ix-integration-blazor/Shared/MainLayout.razor b/src/sanbox/integration/ix-integration-blazor/Shared/MainLayout.razor
index 16173ace5..16c5ec0d4 100644
--- a/src/sanbox/integration/ix-integration-blazor/Shared/MainLayout.razor
+++ b/src/sanbox/integration/ix-integration-blazor/Shared/MainLayout.razor
@@ -1,17 +1,11 @@
-@inherits LayoutComponentBase
+@inherits LayoutComponentBase
-ix-integration-blazor
+AX# RenderableContentControl
-
-
+
+
-
-
-
-
- @Body
-
+
+ @Body
diff --git a/src/sanbox/integration/ix-integration-blazor/Shared/MainLayout.razor.css b/src/sanbox/integration/ix-integration-blazor/Shared/MainLayout.razor.css
deleted file mode 100644
index 551e4b276..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Shared/MainLayout.razor.css
+++ /dev/null
@@ -1,70 +0,0 @@
-.page {
- position: relative;
- display: flex;
- flex-direction: column;
-}
-
-main {
- flex: 1;
-}
-
-.sidebar {
- background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
-}
-
-.top-row {
- background-color: #f7f7f7;
- border-bottom: 1px solid #d6d5d5;
- justify-content: flex-end;
- height: 3.5rem;
- display: flex;
- align-items: center;
-}
-
- .top-row ::deep a, .top-row .btn-link {
- white-space: nowrap;
- margin-left: 1.5rem;
- }
-
- .top-row a:first-child {
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
-@media (max-width: 640.98px) {
- .top-row:not(.auth) {
- display: none;
- }
-
- .top-row.auth {
- justify-content: space-between;
- }
-
- .top-row a, .top-row .btn-link {
- margin-left: 0;
- }
-}
-
-@media (min-width: 641px) {
- .page {
- flex-direction: row;
- }
-
- .sidebar {
- width: 250px;
- height: 100vh;
- position: sticky;
- top: 0;
- }
-
- .top-row {
- position: sticky;
- top: 0;
- z-index: 1;
- }
-
- .top-row, article {
- padding-left: 2rem !important;
- padding-right: 1.5rem !important;
- }
-}
diff --git a/src/sanbox/integration/ix-integration-blazor/Shared/NavMenu.razor b/src/sanbox/integration/ix-integration-blazor/Shared/NavMenu.razor
index 945041b19..f185139ab 100644
--- a/src/sanbox/integration/ix-integration-blazor/Shared/NavMenu.razor
+++ b/src/sanbox/integration/ix-integration-blazor/Shared/NavMenu.razor
@@ -1,85 +1,50 @@
-
+
+
+
+
+
+ AX# RenderableContent
+
-
-
-@code {
- private bool collapseNavMenu = true;
+
+
+
+
+
+ Home
+
+
+
+
+
+ Rendering
+
+
+
+
+
+ Layouts
+
+
+
+
+
+ Customize
+
+
+
+
+
+ Integrate
+
+
+
+
- private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
-
- private void ToggleNavMenu()
- {
- collapseNavMenu = !collapseNavMenu;
- }
-}
+
+
+
+
+
+
+
diff --git a/src/sanbox/integration/ix-integration-blazor/Shared/NavMenu.razor.css b/src/sanbox/integration/ix-integration-blazor/Shared/NavMenu.razor.css
deleted file mode 100644
index acc5f9f81..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Shared/NavMenu.razor.css
+++ /dev/null
@@ -1,62 +0,0 @@
-.navbar-toggler {
- background-color: rgba(255, 255, 255, 0.1);
-}
-
-.top-row {
- height: 3.5rem;
- background-color: rgba(0,0,0,0.4);
-}
-
-.navbar-brand {
- font-size: 1.1rem;
-}
-
-.oi {
- width: 2rem;
- font-size: 1.1rem;
- vertical-align: text-top;
- top: -2px;
-}
-
-.nav-item {
- font-size: 0.9rem;
- padding-bottom: 0.5rem;
-}
-
- .nav-item:first-of-type {
- padding-top: 1rem;
- }
-
- .nav-item:last-of-type {
- padding-bottom: 1rem;
- }
-
- .nav-item ::deep a {
- color: #d7d7d7;
- border-radius: 4px;
- height: 3rem;
- display: flex;
- align-items: center;
- line-height: 3rem;
- }
-
-.nav-item ::deep a.active {
- background-color: rgba(255,255,255,0.25);
- color: white;
-}
-
-.nav-item ::deep a:hover {
- background-color: rgba(255,255,255,0.1);
- color: white;
-}
-
-@media (min-width: 641px) {
- .navbar-toggler {
- display: none;
- }
-
- .collapse {
- /* Never collapse the sidebar for wide screens */
- display: block;
- }
-}
diff --git a/src/sanbox/integration/ix-integration-blazor/Shared/ShowcaseSection.razor b/src/sanbox/integration/ix-integration-blazor/Shared/ShowcaseSection.razor
new file mode 100644
index 000000000..cf3470ce4
--- /dev/null
+++ b/src/sanbox/integration/ix-integration-blazor/Shared/ShowcaseSection.razor
@@ -0,0 +1,44 @@
+
+
+
+
@Title
+ @if (Description != null)
+ {
+
@Description
+ }
+
+ @if (!string.IsNullOrWhiteSpace(Snippet))
+ {
+
+
+ @(showCode ? "Hide code" : "Show code")
+
+ }
+
+
+ @if (showCode && !string.IsNullOrWhiteSpace(Snippet))
+ {
+
+
@SnippetTitle
+
@Snippet.Trim()
+
+ }
+
+
+ @ChildContent
+
+
+
+@code {
+ [Parameter] public string Title { get; set; } = "";
+ [Parameter] public RenderFragment? Description { get; set; }
+ [Parameter] public RenderFragment? ChildContent { get; set; }
+ [Parameter] public string? Snippet { get; set; }
+ [Parameter] public string SnippetTitle { get; set; } = "ST declaration";
+
+ private bool showCode;
+
+ private void ToggleCode() => showCode = !showCode;
+}
diff --git a/src/sanbox/integration/ix-integration-blazor/Shared/Theme.razor b/src/sanbox/integration/ix-integration-blazor/Shared/Theme.razor
new file mode 100644
index 000000000..e567761ec
--- /dev/null
+++ b/src/sanbox/integration/ix-integration-blazor/Shared/Theme.razor
@@ -0,0 +1,16 @@
+@inject ThemeService _themeService
+
+
+ _themeService.SetLightAsync()">
+
+
+ _themeService.SetDarkAsync()">
+
+
+ _themeService.SetSystemAsync()">
+
+
+
diff --git a/src/sanbox/integration/ix-integration-blazor/Shared/TopRow.razor.css b/src/sanbox/integration/ix-integration-blazor/Shared/TopRow.razor.css
deleted file mode 100644
index de1fae53f..000000000
--- a/src/sanbox/integration/ix-integration-blazor/Shared/TopRow.razor.css
+++ /dev/null
@@ -1,13 +0,0 @@
-.top-row {
- background-color: #f7f7f7;
- border-bottom: 1px solid #d6d5d5;
- height: 3.5rem;
- display: flex;
- align-items: center;
-}
-
-@media (max-width: 769px) {
- .IAmHereIndicator {
- display: none;
- }
-}
diff --git a/src/sanbox/integration/ix-integration-blazor/ThemeService.cs b/src/sanbox/integration/ix-integration-blazor/ThemeService.cs
new file mode 100644
index 000000000..13649e21e
--- /dev/null
+++ b/src/sanbox/integration/ix-integration-blazor/ThemeService.cs
@@ -0,0 +1,23 @@
+using Microsoft.JSInterop;
+
+namespace ix_integration_blazor
+{
+ public class ThemeService
+ {
+ private readonly IJSRuntime _js;
+
+ public ThemeService(IJSRuntime js) => _js = js;
+
+ public ValueTask InitAsync() =>
+ _js.InvokeVoidAsync("themeManager.init");
+
+ public ValueTask SetLightAsync() =>
+ _js.InvokeVoidAsync("themeManager.setLight");
+
+ public ValueTask SetDarkAsync() =>
+ _js.InvokeVoidAsync("themeManager.setDark");
+
+ public ValueTask SetSystemAsync() =>
+ _js.InvokeVoidAsync("themeManager.setSystem");
+ }
+}
diff --git a/src/sanbox/integration/ix-integration-blazor/tailwind.ps1 b/src/sanbox/integration/ix-integration-blazor/tailwind.ps1
index 1e583341a..aa16810c0 100644
--- a/src/sanbox/integration/ix-integration-blazor/tailwind.ps1
+++ b/src/sanbox/integration/ix-integration-blazor/tailwind.ps1
@@ -1 +1 @@
-npx @tailwindcss/cli -i .\wwwroot\css\App.css -o .\wwwroot\css\tailwind\tailwind.css --watch --content ".\**\*.{html,js,jsx,ts,tsx,vue,razor}"
+npx @tailwindcss/cli -i .\wwwroot\css\App.css -o .\wwwroot\css\tailwind\tailwind.css --watch --content ".\**\*.{html,cshtml,js,jsx,ts,tsx,vue,razor}"
diff --git a/src/sanbox/integration/ix-integration-blazor/wwwroot/css/App.css b/src/sanbox/integration/ix-integration-blazor/wwwroot/css/App.css
index 9eaafa193..f5ea8bfc1 100644
--- a/src/sanbox/integration/ix-integration-blazor/wwwroot/css/App.css
+++ b/src/sanbox/integration/ix-integration-blazor/wwwroot/css/App.css
@@ -1,28 +1,22 @@
@import "tailwindcss";
-@import "../../Operon/operon.css";
-@source "../../Operon/Components";
-@source "../../Operon/Icons";
+@source "./";
+@import "./operon-variables.css";
+@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
-h1:focus {
- outline: none;
-}
-
-a, .btn-link {
- color: #0071c1;
+.app-nav-link {
+ @apply flex items-center gap-1.5 rounded-md px-2.5 py-1.5 text-text-light transition hover:bg-primary-light hover:text-primary;
}
-.btn-primary {
- color: #fff;
- background-color: #1b6ec2;
- border-color: #1861ac;
+.app-nav-link.active {
+ @apply bg-primary-light text-primary font-medium;
}
-.content {
- padding-top: 1.1rem;
+h1:focus {
+ outline: none;
}
.valid.modified:not([type=checkbox]) {
@@ -36,32 +30,3 @@ a, .btn-link {
.validation-message {
color: red;
}
-
-#blazor-error-ui {
- background: lightyellow;
- bottom: 0;
- box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
- display: none;
- left: 0;
- padding: 0.6rem 1.25rem 0.7rem 1.25rem;
- position: fixed;
- width: 100%;
- z-index: 1000;
-}
-
- #blazor-error-ui .dismiss {
- cursor: pointer;
- position: absolute;
- right: 0.75rem;
- top: 0.5rem;
- }
-
-.blazor-error-boundary {
- background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
- padding: 1rem 1rem 1rem 3.7rem;
- color: white;
-}
-
- .blazor-error-boundary::after {
- content: "An error has occurred."
- }
diff --git a/src/sanbox/integration/ix-integration-blazor/wwwroot/css/tailwind/tailwind.css b/src/sanbox/integration/ix-integration-blazor/wwwroot/css/tailwind/tailwind.css
index 57e158990..a2d2ab0a0 100644
--- a/src/sanbox/integration/ix-integration-blazor/wwwroot/css/tailwind/tailwind.css
+++ b/src/sanbox/integration/ix-integration-blazor/wwwroot/css/tailwind/tailwind.css
@@ -1,4 +1,4 @@
-/*! tailwindcss v4.1.6 | MIT License | https://tailwindcss.com */
+/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
@layer properties;
@layer theme, base, components, utilities;
@layer theme {
@@ -7,143 +7,11 @@
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
- --color-red-50: oklch(97.1% 0.013 17.38);
- --color-red-100: oklch(93.6% 0.032 17.717);
- --color-red-200: oklch(88.5% 0.062 18.334);
- --color-red-300: oklch(80.8% 0.114 19.571);
- --color-red-400: oklch(70.4% 0.191 22.216);
- --color-red-500: oklch(63.7% 0.237 25.331);
- --color-red-600: oklch(57.7% 0.245 27.325);
- --color-red-700: oklch(50.5% 0.213 27.518);
- --color-red-800: oklch(44.4% 0.177 26.899);
- --color-red-900: oklch(39.6% 0.141 25.723);
- --color-red-950: oklch(25.8% 0.092 26.042);
- --color-orange-50: oklch(98% 0.016 73.684);
- --color-orange-100: oklch(95.4% 0.038 75.164);
- --color-orange-200: oklch(90.1% 0.076 70.697);
- --color-orange-300: oklch(83.7% 0.128 66.29);
- --color-orange-400: oklch(75% 0.183 55.934);
- --color-orange-500: oklch(70.5% 0.213 47.604);
- --color-orange-600: oklch(64.6% 0.222 41.116);
- --color-orange-700: oklch(55.3% 0.195 38.402);
- --color-orange-800: oklch(47% 0.157 37.304);
- --color-orange-900: oklch(40.8% 0.123 38.172);
- --color-orange-950: oklch(26.6% 0.079 36.259);
- --color-amber-50: oklch(98.7% 0.022 95.277);
- --color-amber-100: oklch(96.2% 0.059 95.617);
- --color-amber-200: oklch(92.4% 0.12 95.746);
- --color-amber-300: oklch(87.9% 0.169 91.605);
- --color-amber-400: oklch(82.8% 0.189 84.429);
- --color-amber-500: oklch(76.9% 0.188 70.08);
- --color-amber-600: oklch(66.6% 0.179 58.318);
- --color-amber-700: oklch(55.5% 0.163 48.998);
- --color-amber-800: oklch(47.3% 0.137 46.201);
- --color-amber-900: oklch(41.4% 0.112 45.904);
- --color-amber-950: oklch(27.9% 0.077 45.635);
- --color-yellow-500: oklch(79.5% 0.184 86.047);
- --color-yellow-600: oklch(68.1% 0.162 75.834);
- --color-lime-500: oklch(76.8% 0.233 130.85);
- --color-lime-600: oklch(64.8% 0.2 131.684);
- --color-green-50: oklch(98.2% 0.018 155.826);
- --color-green-100: oklch(96.2% 0.044 156.743);
- --color-green-200: oklch(92.5% 0.084 155.995);
- --color-green-300: oklch(87.1% 0.15 154.449);
- --color-green-400: oklch(79.2% 0.209 151.711);
- --color-green-500: oklch(72.3% 0.219 149.579);
- --color-green-600: oklch(62.7% 0.194 149.214);
- --color-green-700: oklch(52.7% 0.154 150.069);
- --color-green-800: oklch(44.8% 0.119 151.328);
- --color-green-900: oklch(39.3% 0.095 152.535);
- --color-green-950: oklch(26.6% 0.065 152.934);
- --color-emerald-50: oklch(97.9% 0.021 166.113);
- --color-emerald-100: oklch(95% 0.052 163.051);
- --color-emerald-200: oklch(90.5% 0.093 164.15);
- --color-emerald-300: oklch(84.5% 0.143 164.978);
- --color-emerald-400: oklch(76.5% 0.177 163.223);
- --color-emerald-500: oklch(69.6% 0.17 162.48);
- --color-emerald-600: oklch(59.6% 0.145 163.225);
- --color-emerald-700: oklch(50.8% 0.118 165.612);
- --color-emerald-800: oklch(43.2% 0.095 166.913);
- --color-emerald-900: oklch(37.8% 0.077 168.94);
- --color-emerald-950: oklch(26.2% 0.051 172.552);
- --color-teal-500: oklch(70.4% 0.14 182.503);
- --color-teal-600: oklch(60% 0.118 184.704);
- --color-cyan-50: oklch(98.4% 0.019 200.873);
- --color-cyan-100: oklch(95.6% 0.045 203.388);
- --color-cyan-200: oklch(91.7% 0.08 205.041);
- --color-cyan-300: oklch(86.5% 0.127 207.078);
- --color-cyan-400: oklch(78.9% 0.154 211.53);
- --color-cyan-500: oklch(71.5% 0.143 215.221);
- --color-cyan-600: oklch(60.9% 0.126 221.723);
- --color-cyan-700: oklch(52% 0.105 223.128);
- --color-cyan-800: oklch(45% 0.085 224.283);
- --color-cyan-900: oklch(39.8% 0.07 227.392);
- --color-cyan-950: oklch(30.2% 0.056 229.695);
- --color-sky-50: oklch(97.7% 0.013 236.62);
- --color-sky-100: oklch(95.1% 0.026 236.824);
- --color-sky-200: oklch(90.1% 0.058 230.902);
- --color-sky-300: oklch(82.8% 0.111 230.318);
- --color-sky-400: oklch(74.6% 0.16 232.661);
- --color-sky-500: oklch(68.5% 0.169 237.323);
- --color-sky-600: oklch(58.8% 0.158 241.966);
- --color-sky-700: oklch(50% 0.134 242.749);
- --color-sky-800: oklch(44.3% 0.11 240.79);
- --color-sky-900: oklch(39.1% 0.09 240.876);
- --color-sky-950: oklch(29.3% 0.066 243.157);
- --color-blue-500: oklch(62.3% 0.214 259.815);
- --color-blue-600: oklch(54.6% 0.245 262.881);
- --color-indigo-500: oklch(58.5% 0.233 277.117);
- --color-indigo-600: oklch(51.1% 0.262 276.966);
- --color-violet-500: oklch(60.6% 0.25 292.717);
- --color-violet-600: oklch(54.1% 0.281 293.009);
- --color-purple-500: oklch(62.7% 0.265 303.9);
- --color-purple-600: oklch(55.8% 0.288 302.321);
- --color-fuchsia-50: oklch(97.7% 0.017 320.058);
- --color-fuchsia-100: oklch(95.2% 0.037 318.852);
- --color-fuchsia-200: oklch(90.3% 0.076 319.62);
- --color-fuchsia-300: oklch(83.3% 0.145 321.434);
- --color-fuchsia-400: oklch(74% 0.238 322.16);
- --color-fuchsia-500: oklch(66.7% 0.295 322.15);
- --color-fuchsia-600: oklch(59.1% 0.293 322.896);
- --color-fuchsia-700: oklch(51.8% 0.253 323.949);
- --color-fuchsia-800: oklch(45.2% 0.211 324.591);
- --color-fuchsia-900: oklch(40.1% 0.17 325.612);
- --color-fuchsia-950: oklch(29.3% 0.136 325.661);
- --color-pink-50: oklch(97.1% 0.014 343.198);
- --color-pink-100: oklch(94.8% 0.028 342.258);
- --color-pink-200: oklch(89.9% 0.061 343.231);
- --color-pink-300: oklch(82.3% 0.12 346.018);
- --color-pink-400: oklch(71.8% 0.202 349.761);
- --color-pink-500: oklch(65.6% 0.241 354.308);
- --color-pink-600: oklch(59.2% 0.249 0.584);
- --color-pink-700: oklch(52.5% 0.223 3.958);
- --color-pink-800: oklch(45.9% 0.187 3.815);
- --color-pink-900: oklch(40.8% 0.153 2.432);
- --color-pink-950: oklch(28.4% 0.109 3.907);
- --color-rose-500: oklch(64.5% 0.246 16.439);
- --color-rose-600: oklch(58.6% 0.253 17.585);
- --color-slate-500: oklch(55.4% 0.046 257.417);
- --color-slate-600: oklch(44.6% 0.043 257.281);
- --color-gray-50: oklch(98.5% 0.002 247.839);
- --color-gray-100: oklch(96.7% 0.003 264.542);
- --color-gray-200: oklch(92.8% 0.006 264.531);
- --color-gray-300: oklch(87.2% 0.01 258.338);
- --color-gray-400: oklch(70.7% 0.022 261.325);
- --color-gray-500: oklch(55.1% 0.027 264.364);
- --color-gray-600: oklch(44.6% 0.03 256.802);
- --color-gray-700: oklch(37.3% 0.034 259.733);
- --color-gray-800: oklch(27.8% 0.033 256.848);
- --color-gray-900: oklch(21% 0.034 264.665);
- --color-zinc-500: oklch(55.2% 0.016 285.938);
- --color-zinc-600: oklch(44.2% 0.017 285.786);
- --color-neutral-500: oklch(55.6% 0 0);
- --color-neutral-600: oklch(43.9% 0 0);
- --color-stone-500: oklch(55.3% 0.013 58.071);
- --color-stone-600: oklch(44.4% 0.011 73.639);
- --color-black: #000;
- --color-white: #fff;
+ --color-black: #0B0C0C;
+ --color-white: #FFFFFF;
--spacing: 0.25rem;
- --container-7xl: 80rem;
+ --breakpoint-xl: 80rem;
+ --container-3xl: 48rem;
--text-xs: 0.75rem;
--text-xs--line-height: calc(1 / 0.75);
--text-sm: 0.875rem;
@@ -157,40 +25,92 @@
--text-2xl: 1.5rem;
--text-2xl--line-height: calc(2 / 1.5);
--text-3xl: 1.875rem;
- --text-3xl--line-height: calc(2.25 / 1.875);
+ --text-3xl--line-height: calc(2 / 1.5);
--text-4xl: 2.25rem;
- --text-4xl--line-height: calc(2.5 / 2.25);
+ --text-4xl--line-height: calc(2 / 1.5);
--text-5xl: 3rem;
- --text-5xl--line-height: 1;
+ --text-5xl--line-height: calc(2 / 1.5);
+ --font-weight-light: 300;
--font-weight-normal: 400;
--font-weight-medium: 500;
+ --font-weight-semibold: 600;
--font-weight-bold: 700;
- --tracking-wider: 0.05em;
+ --tracking-tight: -0.025em;
+ --tracking-wide: 0.025em;
+ --leading-relaxed: 1.625;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
+ --radius-2xl: 1rem;
--default-transition-duration: 150ms;
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
--default-font-family: var(--font-sans);
--default-mono-font-family: var(--font-mono);
- --color-primary-500: var(--color-amber-500);
- --color-primary-600: var(--color-amber-600);
- --color-secondary-500: var(--color-emerald-500);
- --color-secondary-600: var(--color-emerald-600);
- --color-active-500: var(--color-sky-500);
- --color-active-600: var(--color-sky-600);
- --color-inactive-500: var(--color-pink-500);
- --color-inactive-600: var(--color-pink-600);
- --color-info-500: var(--color-cyan-500);
- --color-info-600: var(--color-cyan-600);
- --color-success-500: var(--color-green-500);
- --color-success-600: var(--color-green-600);
- --color-warning-500: var(--color-orange-500);
- --color-warning-600: var(--color-orange-600);
- --color-danger-500: var(--color-red-500);
- --color-danger-600: var(--color-red-600);
- --color-attention-500: var(--color-fuchsia-500);
- --color-attention-600: var(--color-fuchsia-600);
+ --radius-full: calc(infinity * 1px);
+ --color-gray-1: #F3F2F1;
+ --color-gray-2: #DEE0E2;
+ --color-gray-3: #949494;
+ --color-background: #FEFEFE;
+ --color-background-light: #F1F8FF;
+ --color-background-dark: #F0F8FF;
+ --color-background-modal: #F5FAFF;
+ --color-background-modal-light: #FFFFFF;
+ --color-text: #0B0C0C;
+ --color-text-light: #626A6E;
+ --color-border: #BFC1C3;
+ --color-link: #0065B3;
+ --color-link-hover: #003078;
+ --color-primary: #0A319E;
+ --color-primary-light: #E0E8FF;
+ --color-success: #00703C;
+ --color-success-light: #D4F7D4;
+ --color-warning: #EC9811;
+ --color-warning-light: #FFEAC9;
+ --color-danger: #D0190F;
+ --color-danger-light: #FFDCDA;
+ --color-info: #2B8CC4;
+ --color-info-light: #D4ECF7;
+ --color-neutral: #7C7C7C;
+ --color-neutral-light: #DEE0E2;
+ --color-result-failed: var(--color-danger);
+ --color-result-passed: var(--color-success);
+ --color-btn-primary: var(--color-primary);
+ --color-form-text: var(--color-text);
+ --color-form-bg: var(--color-white);
+ --color-form-border: var(--color-border);
+ --color-form-placeholder-text: color-mix(in srgb, #0B0C0C 50%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-placeholder-text: color-mix(in srgb, var(--color-text) 50%, transparent);
+ }
+ --color-form-focus-border: var(--color-primary);
+ --color-form-disabled-text: color-mix(in srgb, #0B0C0C 40%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-disabled-text: color-mix(in srgb, var(--color-text) 40%, transparent);
+ }
+ --color-form-disabled-bg: var(--color-gray-1);
+ --color-form-disabled-border: color-mix(in srgb, #BFC1C3 40%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-disabled-border: color-mix(in srgb, var(--color-border) 40%, transparent);
+ }
+ --color-form-disabled-placeholder-text: color-mix(in srgb, #0B0C0C 20%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-disabled-placeholder-text: color-mix(in srgb, var(--color-text) 20%, transparent);
+ }
+ --color-form-readonly-text: color-mix(in srgb, #0B0C0C 80%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-readonly-text: color-mix(in srgb, var(--color-text) 80%, transparent);
+ }
+ --color-form-readonly-bg: var(--color-gray-1);
+ --color-form-readonly-border: color-mix(in srgb, #BFC1C3 80%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-readonly-border: color-mix(in srgb, var(--color-border) 80%, transparent);
+ }
+ --color-form-readonly-placeholder-text: color-mix(in srgb, #0B0C0C 40%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-readonly-placeholder-text: color-mix(in srgb, var(--color-text) 40%, transparent);
+ }
+ --color-form-invalid: var(--color-danger);
+ --color-input-placeholder-text: var(--color-form-placeholder-text);
}
}
@layer base {
@@ -325,6 +245,9 @@
::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
padding-block: 0;
}
+ ::-webkit-calendar-picker-indicator {
+ line-height: 1;
+ }
:-moz-ui-invalid {
box-shadow: none;
}
@@ -339,33 +262,18 @@
}
}
@layer utilities {
- .collapse {
- visibility: collapse;
- }
- .absolute {
- position: absolute;
- }
- .fixed {
- position: fixed;
- }
- .relative {
- position: relative;
- }
.static {
position: static;
}
- .inset-0 {
- inset: calc(var(--spacing) * 0);
+ .sticky {
+ position: sticky;
+ }
+ .top-0 {
+ top: calc(var(--spacing) * 0);
}
.z-40 {
z-index: 40;
}
- .z-50 {
- z-index: 50;
- }
- .z-500 {
- z-index: 500;
- }
.container {
width: 100%;
@media (width >= 40rem) {
@@ -384,56 +292,44 @@
max-width: 96rem;
}
}
- .-mx-1 {
- margin-inline: calc(var(--spacing) * -1);
- }
- .mx-2 {
- margin-inline: calc(var(--spacing) * 2);
- }
.mx-auto {
margin-inline: auto;
}
- .my-auto {
- margin-block: auto;
- }
- .ms-2 {
- margin-inline-start: calc(var(--spacing) * 2);
- }
- .ms-auto {
- margin-inline-start: auto;
- }
- .me-2 {
- margin-inline-end: calc(var(--spacing) * 2);
- }
- .me-auto {
- margin-inline-end: auto;
+ .-mt-2 {
+ margin-top: calc(var(--spacing) * -2);
}
.mt-1 {
margin-top: calc(var(--spacing) * 1);
}
+ .mt-2 {
+ margin-top: calc(var(--spacing) * 2);
+ }
.mt-3 {
margin-top: calc(var(--spacing) * 3);
}
+ .mt-4 {
+ margin-top: calc(var(--spacing) * 4);
+ }
.mt-6 {
margin-top: calc(var(--spacing) * 6);
}
- .mr-3 {
- margin-right: calc(var(--spacing) * 3);
+ .mt-8 {
+ margin-top: calc(var(--spacing) * 8);
}
- .mb-2 {
- margin-bottom: calc(var(--spacing) * 2);
+ .mt-10 {
+ margin-top: calc(var(--spacing) * 10);
}
- .mb-3 {
- margin-bottom: calc(var(--spacing) * 3);
+ .mt-14 {
+ margin-top: calc(var(--spacing) * 14);
}
- .mb-4 {
- margin-bottom: calc(var(--spacing) * 4);
+ .mb-2 {
+ margin-bottom: calc(var(--spacing) * 2);
}
- .ml-1 {
- margin-left: calc(var(--spacing) * 1);
+ .mb-10 {
+ margin-bottom: calc(var(--spacing) * 10);
}
- .block {
- display: block;
+ .mb-12 {
+ margin-bottom: calc(var(--spacing) * 12);
}
.flex {
display: flex;
@@ -441,11 +337,8 @@
.grid {
display: grid;
}
- .inline-flex {
- display: inline-flex;
- }
- .table {
- display: table;
+ .hidden {
+ display: none;
}
.size-4 {
width: calc(var(--spacing) * 4);
@@ -459,62 +352,85 @@
width: calc(var(--spacing) * 6);
height: calc(var(--spacing) * 6);
}
- .max-h-100 {
- max-height: calc(var(--spacing) * 100);
+ .size-7 {
+ width: calc(var(--spacing) * 7);
+ height: calc(var(--spacing) * 7);
}
- .w-full {
- width: 100%;
+ .h-16 {
+ height: calc(var(--spacing) * 16);
}
- .max-w-7xl {
- max-width: var(--container-7xl);
+ .min-h-screen {
+ min-height: 100vh;
}
- .min-w-4 {
- min-width: calc(var(--spacing) * 4);
+ .w-56 {
+ width: calc(var(--spacing) * 56);
}
- .min-w-6 {
- min-width: calc(var(--spacing) * 6);
+ .max-w-3xl {
+ max-width: var(--container-3xl);
}
- .transform {
- transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
+ .max-w-screen-xl {
+ max-width: var(--breakpoint-xl);
}
- .cursor-pointer {
- cursor: pointer;
+ .flex-1 {
+ flex: 1;
}
- .grid-cols-2 {
- grid-template-columns: repeat(2, minmax(0, 1fr));
+ .shrink-0 {
+ flex-shrink: 0;
+ }
+ .grid-cols-1 {
+ grid-template-columns: repeat(1, minmax(0, 1fr));
}
.flex-col {
flex-direction: column;
}
- .flex-wrap {
- flex-wrap: wrap;
- }
.items-center {
align-items: center;
}
+ .items-end {
+ align-items: flex-end;
+ }
+ .items-start {
+ align-items: flex-start;
+ }
.justify-between {
justify-content: space-between;
}
- .justify-center {
- justify-content: center;
+ .gap-1 {
+ gap: calc(var(--spacing) * 1);
+ }
+ .gap-1\.5 {
+ gap: calc(var(--spacing) * 1.5);
}
.gap-2 {
gap: calc(var(--spacing) * 2);
}
+ .gap-3 {
+ gap: calc(var(--spacing) * 3);
+ }
+ .gap-4 {
+ gap: calc(var(--spacing) * 4);
+ }
.gap-6 {
gap: calc(var(--spacing) * 6);
}
+ .space-y-1 {
+ :where(& > :not(:last-child)) {
+ --tw-space-y-reverse: 0;
+ margin-block-start: calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));
+ margin-block-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)));
+ }
+ }
+ .overflow-hidden {
+ overflow: hidden;
+ }
.overflow-x-auto {
overflow-x: auto;
}
- .overflow-y-auto {
- overflow-y: auto;
- }
.rounded {
border-radius: 0.25rem;
}
.rounded-full {
- border-radius: calc(infinity * 1px);
+ border-radius: var(--radius-full);
}
.rounded-lg {
border-radius: var(--radius-lg);
@@ -522,53 +438,34 @@
.rounded-md {
border-radius: var(--radius-md);
}
+ .rounded-xl {
+ border-radius: var(--radius-xl);
+ }
.border {
border-style: var(--tw-border-style);
border-width: 1px;
}
- .border-4 {
- border-style: var(--tw-border-style);
- border-width: 4px;
- }
- .border-t-2 {
- border-top-style: var(--tw-border-style);
- border-top-width: 2px;
- }
- .border-gray-200 {
- border-color: var(--color-gray-200);
- }
- .border-green-500 {
- border-color: var(--color-green-500);
- }
- .border-transparent {
- border-color: transparent;
- }
- .bg-gray-100 {
- background-color: var(--color-gray-100);
- }
- .bg-gray-200 {
- background-color: var(--color-gray-200);
+ .border-b {
+ border-bottom-style: var(--tw-border-style);
+ border-bottom-width: 1px;
}
- .bg-gray-800\/70 {
- background-color: color-mix(in srgb, oklch(27.8% 0.033 256.848) 70%, transparent);
- @supports (color: color-mix(in lab, red, red)) {
- background-color: color-mix(in oklab, var(--color-gray-800) 70%, transparent);
- }
+ .border-border {
+ border-color: var(--color-border);
}
- .bg-orange-50 {
- background-color: var(--color-orange-50);
+ .bg-background {
+ background-color: var(--color-background);
}
- .bg-primary-500 {
- background-color: var(--color-primary-500);
+ .bg-background-light {
+ background-color: var(--color-background-light);
}
- .bg-white {
- background-color: var(--color-white);
+ .bg-primary-light {
+ background-color: var(--color-primary-light);
}
- .p-1 {
- padding: calc(var(--spacing) * 1);
+ .p-0\.5 {
+ padding: calc(var(--spacing) * 0.5);
}
- .p-2 {
- padding: calc(var(--spacing) * 2);
+ .p-1\.5 {
+ padding: calc(var(--spacing) * 1.5);
}
.p-3 {
padding: calc(var(--spacing) * 3);
@@ -579,8 +476,11 @@
.p-5 {
padding: calc(var(--spacing) * 5);
}
- .px-1 {
- padding-inline: calc(var(--spacing) * 1);
+ .p-6 {
+ padding: calc(var(--spacing) * 6);
+ }
+ .px-1\.5 {
+ padding-inline: calc(var(--spacing) * 1.5);
}
.px-2 {
padding-inline: calc(var(--spacing) * 2);
@@ -591,11 +491,20 @@
.px-4 {
padding-inline: calc(var(--spacing) * 4);
}
- .py-1 {
- padding-block: calc(var(--spacing) * 1);
+ .py-0\.5 {
+ padding-block: calc(var(--spacing) * 0.5);
}
- .ps-3 {
- padding-inline-start: calc(var(--spacing) * 3);
+ .py-1\.5 {
+ padding-block: calc(var(--spacing) * 1.5);
+ }
+ .py-2 {
+ padding-block: calc(var(--spacing) * 2);
+ }
+ .py-8 {
+ padding-block: calc(var(--spacing) * 8);
+ }
+ .py-12 {
+ padding-block: calc(var(--spacing) * 12);
}
.text-center {
text-align: center;
@@ -604,9 +513,17 @@
font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height));
}
- .text-5xl {
- font-size: var(--text-5xl);
- line-height: var(--tw-leading, var(--text-5xl--line-height));
+ .text-3xl {
+ font-size: var(--text-3xl);
+ line-height: var(--tw-leading, var(--text-3xl--line-height));
+ }
+ .text-4xl {
+ font-size: var(--text-4xl);
+ line-height: var(--tw-leading, var(--text-4xl--line-height));
+ }
+ .text-base {
+ font-size: var(--text-base);
+ line-height: var(--tw-leading, var(--text-base--line-height));
}
.text-lg {
font-size: var(--text-lg);
@@ -616,1885 +533,677 @@
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
}
+ .text-xl {
+ font-size: var(--text-xl);
+ line-height: var(--tw-leading, var(--text-xl--line-height));
+ }
.text-xs {
font-size: var(--text-xs);
line-height: var(--tw-leading, var(--text-xs--line-height));
}
+ .leading-relaxed {
+ --tw-leading: var(--leading-relaxed);
+ line-height: var(--leading-relaxed);
+ }
.font-bold {
--tw-font-weight: var(--font-weight-bold);
font-weight: var(--font-weight-bold);
}
+ .font-light {
+ --tw-font-weight: var(--font-weight-light);
+ font-weight: var(--font-weight-light);
+ }
.font-medium {
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
}
- .font-normal {
- --tw-font-weight: var(--font-weight-normal);
- font-weight: var(--font-weight-normal);
+ .font-semibold {
+ --tw-font-weight: var(--font-weight-semibold);
+ font-weight: var(--font-weight-semibold);
}
- .text-blue-500 {
- color: var(--color-blue-500);
+ .tracking-tight {
+ --tw-tracking: var(--tracking-tight);
+ letter-spacing: var(--tracking-tight);
}
- .text-gray-200 {
- color: var(--color-gray-200);
+ .tracking-wide {
+ --tw-tracking: var(--tracking-wide);
+ letter-spacing: var(--tracking-wide);
}
- .text-gray-400 {
- color: var(--color-gray-400);
+ .text-danger {
+ color: var(--color-danger);
}
- .text-gray-500 {
- color: var(--color-gray-500);
+ .text-primary {
+ color: var(--color-primary);
}
- .text-gray-600 {
- color: var(--color-gray-600);
+ .text-success {
+ color: var(--color-success);
}
- .text-gray-700 {
- color: var(--color-gray-700);
+ .text-text {
+ color: var(--color-text);
}
- .text-gray-800 {
- color: var(--color-gray-800);
+ .text-text-light {
+ color: var(--color-text-light);
}
.text-white {
color: var(--color-white);
}
+ .uppercase {
+ text-transform: uppercase;
+ }
.shadow {
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
+ .shadow-md {
+ --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
+ }
+ .shadow-sm {
+ --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
+ }
.transition {
- transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events;
+ transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
- .duration-100 {
- --tw-duration: 100ms;
- transition-duration: 100ms;
- }
- .duration-200 {
- --tw-duration: 200ms;
- transition-duration: 200ms;
+ .group-hover\:text-primary {
+ &:is(:where(.group):hover *) {
+ @media (hover: hover) {
+ color: var(--color-primary);
+ }
+ }
}
- .hover\:bg-gray-200 {
+ .hover\:border-primary {
&:hover {
@media (hover: hover) {
- background-color: var(--color-gray-200);
+ border-color: var(--color-primary);
}
}
}
- .hover\:bg-gray-300 {
+ .hover\:bg-primary-light {
&:hover {
@media (hover: hover) {
- background-color: var(--color-gray-300);
+ background-color: var(--color-primary-light);
}
}
}
- .hover\:bg-primary-600 {
+ .hover\:text-primary {
&:hover {
@media (hover: hover) {
- background-color: var(--color-primary-600);
+ color: var(--color-primary);
}
}
}
+ .focus\:border-primary {
+ &:focus {
+ border-color: var(--color-primary);
+ }
+ }
.focus\:outline-none {
&:focus {
--tw-outline-style: none;
outline-style: none;
}
}
- .sm\:w-80 {
+ .sm\:inline {
+ @media (width >= 40rem) {
+ display: inline;
+ }
+ }
+ .sm\:grid-cols-2 {
+ @media (width >= 40rem) {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+ }
+ .sm\:gap-2 {
+ @media (width >= 40rem) {
+ gap: calc(var(--spacing) * 2);
+ }
+ }
+ .sm\:px-6 {
@media (width >= 40rem) {
- width: calc(var(--spacing) * 80);
+ padding-inline: calc(var(--spacing) * 6);
}
}
- .sm\:flex-row {
+ .sm\:text-5xl {
@media (width >= 40rem) {
- flex-direction: row;
+ font-size: var(--text-5xl);
+ line-height: var(--tw-leading, var(--text-5xl--line-height));
+ }
+ }
+ .md\:inline {
+ @media (width >= 48rem) {
+ display: inline;
+ }
+ }
+ .lg\:grid-cols-2 {
+ @media (width >= 64rem) {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
+ .lg\:grid-cols-3 {
+ @media (width >= 64rem) {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+ }
+ .lg\:grid-cols-4 {
+ @media (width >= 64rem) {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ }
+ }
+ .lg\:px-8 {
+ @media (width >= 64rem) {
+ padding-inline: calc(var(--spacing) * 8);
+ }
+ }
+}
+@layer theme {
+ :root, :host {
+ --font-sans: "Familjen Grotesk", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ }
}
@layer base {
h1 {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
font-size: var(--text-5xl);
line-height: var(--tw-leading, var(--text-5xl--line-height));
--tw-font-weight: var(--font-weight-bold);
font-weight: var(--font-weight-bold);
text-wrap: wrap;
+ overflow-wrap: break-word;
+ color: var(--color-text);
}
h2 {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
font-size: var(--text-4xl);
line-height: var(--tw-leading, var(--text-4xl--line-height));
--tw-font-weight: var(--font-weight-bold);
font-weight: var(--font-weight-bold);
text-wrap: wrap;
+ overflow-wrap: break-word;
+ color: var(--color-text);
}
h3 {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
font-size: var(--text-3xl);
line-height: var(--tw-leading, var(--text-3xl--line-height));
--tw-font-weight: var(--font-weight-bold);
font-weight: var(--font-weight-bold);
text-wrap: wrap;
+ overflow-wrap: break-word;
+ color: var(--color-text);
}
h4 {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height));
--tw-font-weight: var(--font-weight-bold);
font-weight: var(--font-weight-bold);
text-wrap: wrap;
+ overflow-wrap: break-word;
+ color: var(--color-text);
}
h5 {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
font-size: var(--text-xl);
line-height: var(--tw-leading, var(--text-xl--line-height));
--tw-font-weight: var(--font-weight-normal);
font-weight: var(--font-weight-normal);
text-wrap: wrap;
+ overflow-wrap: break-word;
+ color: var(--color-text);
}
h6 {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
font-size: var(--text-lg);
line-height: var(--tw-leading, var(--text-lg--line-height));
--tw-font-weight: var(--font-weight-normal);
font-weight: var(--font-weight-normal);
text-wrap: wrap;
+ overflow-wrap: break-word;
+ color: var(--color-text);
}
p {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
font-size: var(--text-base);
line-height: var(--tw-leading, var(--text-base--line-height));
--tw-font-weight: var(--font-weight-normal);
font-weight: var(--font-weight-normal);
text-wrap: wrap;
+ overflow-wrap: break-word;
+ color: var(--color-text-light);
}
a {
cursor: pointer;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
font-size: var(--text-base);
line-height: var(--tw-leading, var(--text-base--line-height));
--tw-font-weight: var(--font-weight-normal);
font-weight: var(--font-weight-normal);
+ text-wrap: wrap;
+ overflow-wrap: break-word;
+ text-decoration-line: underline;
+ color: var(--color-link);
&:hover {
@media (hover: hover) {
- --tw-font-weight: var(--font-weight-medium);
- font-weight: var(--font-weight-medium);
+ color: var(--color-link-hover);
}
}
}
hr {
+ color: var(--color-text-light);
opacity: 30%;
}
+ span {
+ font-size: var(--text-base);
+ line-height: var(--tw-leading, var(--text-base--line-height));
+ --tw-font-weight: var(--font-weight-normal);
+ font-weight: var(--font-weight-normal);
+ text-wrap: wrap;
+ overflow-wrap: break-word;
+ color: var(--color-text);
+ }
+ div {
+ font-size: var(--text-base);
+ line-height: var(--tw-leading, var(--text-base--line-height));
+ --tw-font-weight: var(--font-weight-normal);
+ font-weight: var(--font-weight-normal);
+ text-wrap: wrap;
+ overflow-wrap: break-word;
+ color: var(--color-text);
+ }
+ input[type="date"], input[type="time"], input[type="datetime-local"] {
+ color-scheme: light;
+ }
+ [data-theme="dark"] input[type="date"], [data-theme="dark"] input[type="time"], [data-theme="dark"] input[type="datetime-local"] {
+ color-scheme: dark;
+ }
+}
+[data-theme="dark"] {
+ --color-black: #FFFFFF;
+ --color-white: #121212;
+ --color-gray-1: #1F1F1F;
+ --color-gray-2: #949494;
+ --color-gray-3: #F3F2F1;
+ --color-background: #121212;
+ --color-background-light: #1F1F1F;
+ --color-background-dark: #1F1F1F;
+ --color-background-modal: #1E1E1E;
+ --color-background-modal-light: #252525;
+ --color-text: #F2F2F2;
+ --color-text-light: #626A6E;
+ --color-border: #BFC1C3 /*#4B5563*/;
+ --color-border-input: #F2F2F2 /*#F2F2F2*/;
+ --color-link: #0065B3 /*#60A5FA*/;
+ --color-link-hover: #89C4FF /*#93C5FD*/;
+ --color-link-visited: #A08AE1 /*#C4B5FD*/;
+ --color-primary: #89C4FF;
+ --color-primary-light: #0A319E /*#1e3a8a*/;
+ --color-success: #4ADE80;
+ --color-success-light: #14532d;
+ --color-warning: #FBBF24;
+ --color-warning-light: #78350f;
+ --color-danger: #F87171;
+ --color-danger-light: #7f1d1d;
+ --color-info: #38BDF8;
+ --color-info-light: #0c4a6e;
+ --color-noactive: #A1A1AA;
+ --color-noactive-light: #27272a;
+ --color-result-failed: #F87171;
+ --color-result-passed: #4ADE80;
+ --color-result-inprogress: #60A5FA;
+ --color-result-exclude: #A78BFA;
+ --color-result-bypasse: #E879F9;
+ --color-result-inconcllusive: #FCD34D;
+ --color-result-runnig: #2DD4BF;
+ --color-result-noaction: #A1A1AA;
+ --color-diagnostic-debug: #60A5FA;
+ --color-diagnostic-verbose: #5EEAD4;
+ --color-diagnostic-information: #38BDF8;
+ --color-diagnostic-warning: #FBBF24;
+ --color-diagnostic-error: #F87171;
+ --color-diagnostic-fatal: #EF4444;
+ --color-automat: #4ADE80;
+ --color-ground: #38BDF8;
+ --color-grounded: #60A5FA;
+ --color-idle: #A1A1AA;
+ --color-manual: #FBBF24;
+ --color-oee: #89C4FF;
+ --color-availability: #B6D069;
+ --color-performance: #A08AE1;
+ --color-quality: #FBBF24;
+ --color-downtime-unassigned: #C7C7C7;
+ --color-downtime-material: #FBBF24;
+ --color-downtime-operator: #F08584;
+ --color-downtime-working: #B6D069;
+ --color-downtime-technical: #A08AE1;
+ --color-downtime-planned: #7DCDD2;
+ --color-btn-primary: var(--color-primary);
+ --color-btn-primary-hover: var(--color-primary-light);
+ --color-btn-danger: var(--color-danger);
+ --color-btn-danger-hover: var(--color-danger-light);
+ --color-btn-success: var(--color-success);
+ --color-btn-success-hover: var(--color-success-light);
+ --color-btn-warning: var(--color-warning);
+ --color-btn-warning-hover: var(--color-warning-light);
+ --color-btn-info: var(--color-info);
+ --color-btn-info-hover: var(--color-info-light);
+ --color-btn-neutral: var(--color-neutral);
+ --color-btn-neutral-hover: var(--color-neutral-light);
+ --color-btn-outline-bg: transparent;
+ --color-btn-no-bg-bg: transparent;
+ --color-action-btn-text: var(--color-text);
+ --color-action-btn-bg: color-mix(in srgb, #FEFEFE 60%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-action-btn-bg: color-mix(in srgb, var(--color-background) 60%, transparent);
+ }
+ --color-action-btn-border: var(--color-border);
+ --color-action-btn-hover-bg: color-mix(in srgb, #F0F8FF 70%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-action-btn-hover-bg: color-mix(in srgb, var(--color-background-dark) 70%, transparent);
+ }
+ --color-action-btn-active-bg: color-mix(in srgb, #0A319E 40%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-action-btn-active-bg: color-mix(in srgb, var(--color-primary) 40%, transparent);
+ }
+ --color-form-text: var(--color-text);
+ --color-form-bg: var(--color-white);
+ --color-form-border: var(--color-border);
+ --color-form-placeholder-text: color-mix(in srgb, #0B0C0C 50%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-placeholder-text: color-mix(in srgb, var(--color-text) 50%, transparent);
+ }
+ --color-form-focus-border: var(--color-primary);
+ --color-form-disabled-text: color-mix(in srgb, #0B0C0C 40%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-disabled-text: color-mix(in srgb, var(--color-text) 40%, transparent);
+ }
+ --color-form-disabled-bg: var(--color-gray-1);
+ --color-form-disabled-border: color-mix(in srgb, #BFC1C3 40%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-disabled-border: color-mix(in srgb, var(--color-border) 40%, transparent);
+ }
+ --color-form-disabled-placeholder-text: color-mix(in srgb, #0B0C0C 20%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-disabled-placeholder-text: color-mix(in srgb, var(--color-text) 20%, transparent);
+ }
+ --color-form-readonly-text: color-mix(in srgb, #0B0C0C 80%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-readonly-text: color-mix(in srgb, var(--color-text) 80%, transparent);
+ }
+ --color-form-readonly-bg: var(--color-gray-1);
+ --color-form-readonly-border: color-mix(in srgb, #BFC1C3 80%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-readonly-border: color-mix(in srgb, var(--color-border) 80%, transparent);
+ }
+ --color-form-readonly-placeholder-text: color-mix(in srgb, #0B0C0C 40%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-form-readonly-placeholder-text: color-mix(in srgb, var(--color-text) 40%, transparent);
+ }
+ --color-form-invalid: var(--color-danger);
+ --color-input-text: var(--color-form-text);
+ --color-input-bg: var(--color-form-bg);
+ --color-input-border: var(--color-form-border);
+ --color-input-placeholder-text: var(--color-form-placeholder-text);
+ --color-input-focus-border: var(--color-form-focus-border);
+ --color-input-disabled-text: var(--color-form-disabled-text);
+ --color-input-disabled-bg: var(--color-form-disabled-bg);
+ --color-input-disabled-border: var(--color-form-disabled-border);
+ --color-input-disabled-placeholder-text: var(--color-form-disabled-placeholder-text);
+ --color-input-readonly-text: var(--color-form-readonly-text);
+ --color-input-readonly-bg: var(--color-form-readonly-bg);
+ --color-input-readonly-border: var(--color-form-readonly-border);
+ --color-input-readonly-placeholder-text: var(--color-form-readonly-placeholder-text);
+ --color-input-invalid: var(--color-form-invalid);
+ --color-feedback-invalid: var(--color-danger);
+ --color-feedback-valid: var(--color-success);
+ --color-feedback: var(--color-text);
+ --color-textarea-text: var(--color-form-text);
+ --color-textarea-bg: var(--color-white);
+ --color-textarea-border: var(--color-form-border);
+ --color-textarea-placeholder-text: var(--color-input-placeholder-text);
+ --color-textarea-focus-border: var(--color-form-focus-border);
+ --color-textarea-disabled-text: var(--color-form-disabled-text);
+ --color-textarea-disabled-bg: var(--color-form-disabled-bg);
+ --color-textarea-disabled-border: var(--color-form-disabled-border);
+ --color-textarea-disabled-placeholder-text: var(--color-form-disabled-placeholder-text);
+ --color-textarea-readonly-text: var(--color-form-readonly-text);
+ --color-textarea-readonly-bg: var(--color-form-readonly-bg);
+ --color-textarea-readonly-border: var(--color-form-readonly-border);
+ --color-textarea-readonly-placeholder-text: var(--color-form-readonly-placeholder-text);
+ --color-textarea-invalid: var(--color-form-invalid);
+ --color-checkbox-bg: var(--color-white);
+ --color-checkbox-border: var(--color-form-border);
+ --color-checkbox-focus-border: var(--color-form-focus-border);
+ --color-checkbox-disabled-bg: var(--color-form-disabled-bg);
+ --color-checkbox-disabled-border: var(--color-form-disabled-border);
+ --color-checkbox-check: var(--color-primary);
+ --color-checkbox-invalid: var(--color-form-invalid);
+ --color-toggle-bg: var(--color-gray-2);
+ --color-toggle-thumb: var(--color-white);
+ --color-toggle-focus: var(--color-primary);
+ --color-toggle-text-off: var(--color-text);
+ --color-toggle-text-on: var(--color-primary);
+ --color-toggle-invalid: var(--color-form-invalid);
+ --color-radio-bg: var(--color-white);
+ --color-radio-border: var(--color-form-border);
+ --color-radio-focus-border: var(--color-form-focus-border);
+ --color-radio-disabled-bg: var(--color-form-disabled-bg);
+ --color-radio-disabled-border: var(--color-form-disabled-border);
+ --color-radio-fill: var(--color-primary);
+ --color-radio-invalid: var(--color-form-invalid);
+ --color-select-text: var(--color-form-text);
+ --color-select-bg: var(--color-white);
+ --color-select-border: var(--color-form-border);
+ --color-select-placeholder-text: var(--color-input-placeholder-text);
+ --color-select-focus-border: var(--color-form-focus-border);
+ --color-select-disabled-text: var(--color-form-disabled-text);
+ --color-select-disabled-bg: var(--color-form-disabled-bg);
+ --color-select-disabled-border: var(--color-form-disabled-border);
+ --color-select-disabled-placeholder-text: var(--color-form-disabled-placeholder-text);
+ --color-select-invalid: var(--color-form-invalid);
+ --color-file-text: var(--color-form-text);
+ --color-file-border: var(--color-form-border);
+ --color-file-button-text: var(--color-form-text);
+ --color-file-button-bg: var(--color-background-dark);
+ --color-color-border: var(--color-form-border);
+ --color-nav-item-active-text: var(--color-primary);
+ --color-nav-item-ctive-border: var(--color-primary);
+ --color-nav-item-text: color-mix(in srgb, #0B0C0C 80%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-nav-item-text: color-mix(in srgb, var(--color-text) 80%, transparent);
+ }
+ --color-nav-item-description-text: color-mix(in srgb, #0B0C0C 60%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-nav-item-description-text: color-mix(in srgb, var(--color-text) 60%, transparent);
+ }
+ --color-breadcrumb-active-text: var(--color-text);
+ --color-card-title-text: var(--color-primary);
+ --color-simple-border-border: var(--color-border);
+ --color-alert-text: var(--color-text);
+ --color-alert-primary-bg: color-mix(in srgb, #0A319E 20%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-alert-primary-bg: color-mix(in srgb, var(--color-primary) 20%, transparent);
+ }
+ --color-alert-success-bg: color-mix(in srgb, #00703C 20%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-alert-success-bg: color-mix(in srgb, var(--color-success) 20%, transparent);
+ }
+ --color-alert-warning-bg: color-mix(in srgb, #EC9811 20%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-alert-warning-bg: color-mix(in srgb, var(--color-warning) 20%, transparent);
+ }
+ --color-alert-danger-bg: color-mix(in srgb, #D0190F 20%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-alert-danger-bg: color-mix(in srgb, var(--color-danger) 20%, transparent);
+ }
+ --color-alert-info-bg: color-mix(in srgb, #2B8CC4 20%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-alert-info-bg: color-mix(in srgb, var(--color-info) 20%, transparent);
+ }
+ --color-list-group-text: var(--color-primary);
+ --color-list-group-bg: var(--color-white);
+ --color-list-group-border: var(--color-border);
+ --color-list-group-item-border: var(--color-border);
+ --color-list-group-item-action-text: var(--color-link);
+ --color-list-group-item-action-hover-text: var(--color-link-hover);
+ --color-list-group-item-action-disabled-text: color-mix(in srgb, #0B0C0C 80%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-list-group-item-action-disabled-text: color-mix(in srgb, var(--color-text) 80%, transparent);
+ }
+ --color-tab-bg: color-mix(in srgb, #F0F8FF 70%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-tab-bg: color-mix(in srgb, var(--color-background-dark) 70%, transparent);
+ }
+ --color-tab-btn-text: var(--color-text);
+ --color-tab-btn-bg: var(--color-background-dark);
+ --color-tab-btn-border: var(--color-border);
+ --color-tab-btn-hover-bg: color-mix(in srgb, #FEFEFE 40%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-tab-btn-hover-bg: color-mix(in srgb, var(--color-background) 40%, transparent);
+ }
+ --color-tab-btn-active-text: var(--color-btn-primary);
+ --color-tab-btn-active-bg: var(--color-background);
+ --color-tab-btn-active-border: var(--color-border);
+ --color-dropdown-body-bg: var(--color-background-modal-light);
+ --color-dropdown-body-border: var(--color-border);
+ --color-dropdown-item-text: color-mix(in srgb, #0B0C0C 70%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-dropdown-item-text: color-mix(in srgb, var(--color-text) 70%, transparent);
+ }
+ --color-dropdown-item-hover-text: color-mix(in srgb, #0B0C0C 90%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-dropdown-item-hover-text: color-mix(in srgb, var(--color-text) 90%, transparent);
+ }
+ --color-tooltip-bg: var(--color-background-modal);
+ --color-tooltip-border: var(--color-primary);
+ --color-modal-bg: var(--color-background-modal);
+ --color-modal-border: var(--color-border);
+ --color-modal-overlay-bg: color-mix(in srgb, #FFFFFF 60%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-modal-overlay-bg: color-mix(in srgb, var(--color-white) 60%, transparent);
+ }
+ --color-accordion-border: var(--color-border);
+ --color-accordion-button-text: color-mix(in srgb, #0A319E 80%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-accordion-button-text: color-mix(in srgb, var(--color-primary) 80%, transparent);
+ }
+ --color-accordion-button-hover-text: var(--color-primary);
+ --color-toast-bg: var(--color-background-modal);
+ --color-toast-divider-text: var(--color-text);
+ --color-spinner-text: color-mix(in srgb, #F0F8FF 70%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-spinner-text: color-mix(in srgb, var(--color-background-dark) 70%, transparent);
+ }
+ --color-spinner-fill: color-mix(in srgb, #0B0C0C 70%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-spinner-fill: color-mix(in srgb, var(--color-text) 70%, transparent);
+ }
+ --color-table-h-text: var(--color-text);
+ --color-table-d-text: var(--color-text);
+ --color-table-striped-odd-bg: color-mix(in srgb, #F0F8FF 20%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-table-striped-odd-bg: color-mix(in srgb, var(--color-background-dark) 20%, transparent);
+ }
+ --color-table-striped-even-bg: color-mix(in srgb, #F0F8FF 80%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-table-striped-even-bg: color-mix(in srgb, var(--color-background-dark) 80%, transparent);
+ }
+ --color-table-hover-bg: color-mix(in srgb, #F0F8FF 90%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-table-hover-bg: color-mix(in srgb, var(--color-background-dark) 90%, transparent);
+ }
+ --color-pagination-number-text: color-mix(in srgb, #0B0C0C 60%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-pagination-number-text: color-mix(in srgb, var(--color-text) 60%, transparent);
+ }
+ --color-pagination-number-hover-bg: color-mix(in srgb, #0B0C0C 10%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-pagination-number-hover-bg: color-mix(in srgb, var(--color-text) 10%, transparent);
+ }
+ --color-pagination-number-active-bg: color-mix(in srgb, #0B0C0C 20%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-pagination-number-active-bg: color-mix(in srgb, var(--color-text) 20%, transparent);
+ }
+ --color-pagination-number-disabled-text: color-mix(in srgb, #0B0C0C 40%, transparent);
+ @supports (color: color-mix(in lab, red, red)) {
+ --color-pagination-number-disabled-text: color-mix(in srgb, var(--color-text) 40%, transparent);
+ }
+}
+:root {
+ --btn-hover-translate: -0.1rem;
+}
+html, body {
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
-.nav {
+.app-nav-link {
display: flex;
- border-top-right-radius: var(--radius-lg);
- border-bottom-right-radius: var(--radius-lg);
- border-left-style: var(--tw-border-style);
- border-left-width: 2px;
- border-color: var(--color-gray-300);
- padding-block: calc(var(--spacing) * 2);
- padding-inline-start: calc(var(--spacing) * 3);
- color: var(--color-gray-500);
- transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events;
+ align-items: center;
+ gap: calc(var(--spacing) * 1.5);
+ border-radius: var(--radius-md);
+ padding-inline: calc(var(--spacing) * 2.5);
+ padding-block: calc(var(--spacing) * 1.5);
+ color: var(--color-text-light);
+ transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
- --tw-duration: 200ms;
- transition-duration: 200ms;
&:hover {
@media (hover: hover) {
- border-color: var(--color-gray-500);
+ background-color: var(--color-primary-light);
}
}
&:hover {
@media (hover: hover) {
- color: var(--color-gray-800);
+ color: var(--color-primary);
}
}
}
-.nav-collapse-button {
- transform: rotate(0deg);
- transition: transform .2s linear;
-}
-.nav-collapse-button.open {
- transform: rotate(180deg);
- transition: transform .2s linear;
-}
-.nav-active {
- border-color: var(--color-gray-600);
- --tw-font-weight: var(--font-weight-bold);
- font-weight: var(--font-weight-bold);
- color: var(--color-black);
-}
-.simple-border {
- border-radius: var(--radius-xl);
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-gray-300);
- background-clip: border-box;
- padding: calc(var(--spacing) * 2);
-}
-.btn {
- display: inline-flex;
- cursor: pointer;
- border-radius: var(--radius-lg);
- padding-inline: calc(var(--spacing) * 4);
- padding-block: calc(var(--spacing) * 2);
+.app-nav-link.active {
+ background-color: var(--color-primary-light);
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
- transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events;
- transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
- transition-duration: var(--tw-duration, var(--default-transition-duration));
- --tw-duration: 200ms;
- transition-duration: 200ms;
- &:disabled {
- pointer-events: none;
- }
- &:disabled {
- opacity: 70%;
- }
+ color: var(--color-primary);
}
-.btn-red {
- background-color: var(--color-red-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-red-600);
- }
- }
+h1:focus {
+ outline: none;
}
-.btn-outline-red {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-red-500);
- background-color: transparent;
- color: var(--color-red-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-red-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
+.valid.modified:not([type=checkbox]) {
+ outline: 1px solid #26b050;
}
-.btn-no-bg-red {
- background-color: transparent;
- color: var(--color-red-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
+.invalid {
+ outline: 1px solid red;
}
-.btn-orange {
- background-color: var(--color-orange-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-orange-600);
- }
- }
+.validation-message {
+ color: red;
}
-.btn-outline-orange {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-orange-500);
- background-color: transparent;
- color: var(--color-orange-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-orange-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
+@property --tw-space-y-reverse {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0;
}
-.btn-no-bg-orange {
- background-color: transparent;
- color: var(--color-orange-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
+@property --tw-border-style {
+ syntax: "*";
+ inherits: false;
+ initial-value: solid;
}
-.btn-amber {
- background-color: var(--color-amber-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-amber-600);
- }
- }
+@property --tw-leading {
+ syntax: "*";
+ inherits: false;
}
-.btn-outline-amber {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-amber-500);
- background-color: transparent;
- color: var(--color-amber-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-amber-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
+@property --tw-font-weight {
+ syntax: "*";
+ inherits: false;
}
-.btn-no-bg-amber {
- background-color: transparent;
- color: var(--color-amber-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
+@property --tw-tracking {
+ syntax: "*";
+ inherits: false;
}
-.btn-yellow {
- background-color: var(--color-yellow-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-yellow-600);
- }
- }
+@property --tw-shadow {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0 0 #0000;
}
-.btn-outline-yellow {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-yellow-500);
- background-color: transparent;
- color: var(--color-yellow-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-yellow-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
+@property --tw-shadow-color {
+ syntax: "*";
+ inherits: false;
}
-.btn-no-bg-yellow {
- background-color: transparent;
- color: var(--color-yellow-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-lime {
- background-color: var(--color-lime-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-lime-600);
- }
- }
-}
-.btn-outline-lime {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-lime-500);
- background-color: transparent;
- color: var(--color-lime-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-lime-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-lime {
- background-color: transparent;
- color: var(--color-lime-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-green {
- background-color: var(--color-green-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-green-600);
- }
- }
-}
-.btn-outline-green {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-green-500);
- background-color: transparent;
- color: var(--color-green-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-green-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-green {
- background-color: transparent;
- color: var(--color-green-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-emerald {
- background-color: var(--color-emerald-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-emerald-600);
- }
- }
-}
-.btn-outline-emerald {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-emerald-500);
- background-color: transparent;
- color: var(--color-emerald-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-emerald-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-emerald {
- background-color: transparent;
- color: var(--color-emerald-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-teal {
- background-color: var(--color-teal-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-teal-600);
- }
- }
-}
-.btn-outline-teal {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-teal-500);
- background-color: transparent;
- color: var(--color-teal-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-teal-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-teal {
- background-color: transparent;
- color: var(--color-teal-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-cyan {
- background-color: var(--color-cyan-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-cyan-600);
- }
- }
-}
-.btn-outline-cyan {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-cyan-500);
- background-color: transparent;
- color: var(--color-cyan-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-cyan-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-cyan {
- background-color: transparent;
- color: var(--color-cyan-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-sky {
- background-color: var(--color-sky-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-sky-600);
- }
- }
-}
-.btn-outline-sky {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-sky-500);
- background-color: transparent;
- color: var(--color-sky-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-sky-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-sky {
- background-color: transparent;
- color: var(--color-sky-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-blue {
- background-color: var(--color-blue-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-blue-600);
- }
- }
-}
-.btn-outline-blue {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-blue-500);
- background-color: transparent;
- color: var(--color-blue-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-blue-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-blue {
- background-color: transparent;
- color: var(--color-blue-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-indigo {
- background-color: var(--color-indigo-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-indigo-600);
- }
- }
-}
-.btn-outline-indigo {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-indigo-500);
- background-color: transparent;
- color: var(--color-indigo-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-indigo-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-indigo {
- background-color: transparent;
- color: var(--color-indigo-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-violet {
- background-color: var(--color-violet-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-violet-600);
- }
- }
-}
-.btn-outline-violet {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-violet-500);
- background-color: transparent;
- color: var(--color-violet-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-violet-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-violet {
- background-color: transparent;
- color: var(--color-violet-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-purple {
- background-color: var(--color-purple-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-purple-600);
- }
- }
-}
-.btn-outline-purple {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-purple-500);
- background-color: transparent;
- color: var(--color-purple-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-purple-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-purple {
- background-color: transparent;
- color: var(--color-purple-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-fuchsia {
- background-color: var(--color-fuchsia-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-fuchsia-600);
- }
- }
-}
-.btn-outline-fuchsia {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-fuchsia-500);
- background-color: transparent;
- color: var(--color-fuchsia-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-fuchsia-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-fuchsia {
- background-color: transparent;
- color: var(--color-fuchsia-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-pink {
- background-color: var(--color-pink-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-pink-600);
- }
- }
-}
-.btn-outline-pink {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-pink-500);
- background-color: transparent;
- color: var(--color-pink-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-pink-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-pink {
- background-color: transparent;
- color: var(--color-pink-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-rose {
- background-color: var(--color-rose-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-rose-600);
- }
- }
-}
-.btn-outline-rose {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-rose-500);
- background-color: transparent;
- color: var(--color-rose-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-rose-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-rose {
- background-color: transparent;
- color: var(--color-rose-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-slate {
- background-color: var(--color-slate-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-slate-600);
- }
- }
-}
-.btn-outline-slate {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-slate-500);
- background-color: transparent;
- color: var(--color-slate-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-slate-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-slate {
- background-color: transparent;
- color: var(--color-slate-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-gray {
- background-color: var(--color-gray-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-600);
- }
- }
-}
-.btn-outline-gray {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-gray-500);
- background-color: transparent;
- color: var(--color-gray-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-gray {
- background-color: transparent;
- color: var(--color-gray-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-zinc {
- background-color: var(--color-zinc-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-zinc-600);
- }
- }
-}
-.btn-outline-zinc {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-zinc-500);
- background-color: transparent;
- color: var(--color-zinc-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-zinc-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-zinc {
- background-color: transparent;
- color: var(--color-zinc-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-neutral {
- background-color: var(--color-neutral-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-neutral-600);
- }
- }
-}
-.btn-outline-neutral {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-neutral-500);
- background-color: transparent;
- color: var(--color-neutral-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-neutral-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-neutral {
- background-color: transparent;
- color: var(--color-neutral-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-stone {
- background-color: var(--color-stone-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-stone-600);
- }
- }
-}
-.btn-outline-stone {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-stone-500);
- background-color: transparent;
- color: var(--color-stone-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-stone-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-stone {
- background-color: transparent;
- color: var(--color-stone-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-primary {
- background-color: var(--color-primary-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-primary-600);
- }
- }
-}
-.btn-outline-primary {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-primary-500);
- background-color: transparent;
- color: var(--color-primary-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-primary-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-primary {
- background-color: transparent;
- color: var(--color-primary-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-secondary {
- background-color: var(--color-secondary-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-secondary-600);
- }
- }
-}
-.btn-outline-secondary {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-secondary-500);
- background-color: transparent;
- color: var(--color-secondary-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-secondary-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-secondary {
- background-color: transparent;
- color: var(--color-secondary-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-active {
- background-color: var(--color-active-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-active-600);
- }
- }
-}
-.btn-outline-active {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-active-500);
- background-color: transparent;
- color: var(--color-active-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-active-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-active {
- background-color: transparent;
- color: var(--color-active-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-inactive {
- background-color: var(--color-inactive-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-inactive-600);
- }
- }
-}
-.btn-outline-inactive {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-inactive-500);
- background-color: transparent;
- color: var(--color-inactive-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-inactive-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-inactive {
- background-color: transparent;
- color: var(--color-inactive-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-info {
- background-color: var(--color-info-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-info-600);
- }
- }
-}
-.btn-outline-info {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-info-500);
- background-color: transparent;
- color: var(--color-info-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-info-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-info {
- background-color: transparent;
- color: var(--color-info-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-success {
- background-color: var(--color-success-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-success-600);
- }
- }
-}
-.btn-outline-success {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-success-500);
- background-color: transparent;
- color: var(--color-success-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-success-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-success {
- background-color: transparent;
- color: var(--color-success-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-warning {
- background-color: var(--color-warning-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-warning-600);
- }
- }
-}
-.btn-outline-warning {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-warning-500);
- background-color: transparent;
- color: var(--color-warning-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-warning-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-warning {
- background-color: transparent;
- color: var(--color-warning-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-danger {
- background-color: var(--color-danger-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-danger-600);
- }
- }
-}
-.btn-outline-danger {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-danger-500);
- background-color: transparent;
- color: var(--color-danger-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-danger-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-danger {
- background-color: transparent;
- color: var(--color-danger-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-attention {
- background-color: var(--color-attention-500);
- color: var(--color-white);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-attention-600);
- }
- }
-}
-.btn-outline-attention {
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-attention-500);
- background-color: transparent;
- color: var(--color-attention-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-attention-500);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-white);
- }
- }
-}
-.btn-no-bg-attention {
- background-color: transparent;
- color: var(--color-attention-500);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.btn-group {
- display: inline-flex;
- flex-wrap: wrap;
- align-items: center;
- border-radius: var(--radius-lg);
-}
-.btn-group > .btn {
- border-radius: 0;
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-gray-200);
-}
-.btn-group > .btn:first-child {
- border-top-left-radius: var(--radius-lg);
- border-bottom-left-radius: var(--radius-lg);
-}
-.btn-group > .btn:last-child {
- border-top-right-radius: var(--radius-lg);
- border-bottom-right-radius: var(--radius-lg);
-}
-.disabled {
- color: var(--color-gray-500);
-}
-.invalid-feedback:not(.is-invalid) {
- display: none;
-}
-.invalid-feedback {
- margin-inline-start: calc(var(--spacing) * 1);
- font-size: var(--text-sm);
- line-height: var(--tw-leading, var(--text-sm--line-height));
-}
-.is-invalid {
- color: var(--color-red-500);
-}
-.input {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
-}
-.input > input {
- margin-block: calc(var(--spacing) * 1);
- max-width: 100%;
- border-radius: var(--radius-lg);
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-gray-200);
- padding-inline: calc(var(--spacing) * 3);
- padding-block: calc(var(--spacing) * 1);
- &:focus {
- outline-style: var(--tw-outline-style);
- outline-width: 2px;
- }
- &:focus {
- outline-color: var(--color-primary-500);
- }
-}
-.input > label {
- font-size: var(--text-sm);
- line-height: var(--tw-leading, var(--text-sm--line-height));
- color: var(--color-gray-800);
-}
-.input-checkbox {
- margin-block: calc(var(--spacing) * 1);
- display: flex;
- align-items: center;
-}
-.input-checkbox > label:first-child {
- position: relative;
- display: flex;
- cursor: pointer;
- align-items: center;
-}
-.input-checkbox > label:first-child > input {
- height: calc(var(--spacing) * 5);
- width: calc(var(--spacing) * 5);
- cursor: pointer;
- appearance: none;
- border-radius: 0.25rem;
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-gray-200);
- transition-property: all;
- transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
- transition-duration: var(--tw-duration, var(--default-transition-duration));
- &:checked {
- border-color: var(--color-primary-500);
- }
- &:checked {
- background-color: var(--color-primary-500);
- }
-}
-.input-checkbox > label:first-child > span {
- position: absolute;
- top: calc(1/2 * 100%);
- left: calc(1/2 * 100%);
- --tw-translate-x: calc(calc(1/2 * 100%) * -1);
- translate: var(--tw-translate-x) var(--tw-translate-y);
- --tw-translate-y: calc(calc(1/2 * 100%) * -1);
- translate: var(--tw-translate-x) var(--tw-translate-y);
- transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
- color: var(--color-white);
- opacity: 0%;
- &:is(:where(.peer):checked ~ *) {
- opacity: 100%;
- }
-}
-.input-checkbox > label:last-child {
- margin-left: calc(var(--spacing) * 2);
- cursor: pointer;
- font-size: var(--text-sm);
- line-height: var(--tw-leading, var(--text-sm--line-height));
- color: var(--color-gray-800);
-}
-.input-radio {
- display: flex;
- align-items: center;
- column-gap: calc(var(--spacing) * 3);
-}
-.input-radio > input {
- position: relative;
- width: calc(var(--spacing) * 4);
- height: calc(var(--spacing) * 4);
- appearance: none;
- border-radius: calc(infinity * 1px);
- border-style: var(--tw-border-style);
- border-width: 1px;
- border-color: var(--color-gray-200);
- &::before {
- content: var(--tw-content);
- position: absolute;
- }
- &::before {
- content: var(--tw-content);
- inset: calc(var(--spacing) * 1);
- }
- &::before {
- content: var(--tw-content);
- border-radius: calc(infinity * 1px);
- }
- &::before {
- content: var(--tw-content);
- background-color: var(--color-white);
- }
- &:not(*:checked) {
- &::before {
- content: var(--tw-content);
- display: none;
- }
- }
- &:checked {
- border-color: var(--color-primary-500);
- }
- &:checked {
- background-color: var(--color-primary-500);
- }
- &:focus-visible {
- outline-style: var(--tw-outline-style);
- outline-width: 2px;
- }
- &:focus-visible {
- outline-offset: 2px;
- }
- &:focus-visible {
- outline-color: var(--color-primary-500);
- }
- &:disabled {
- border-color: var(--color-gray-200);
- }
- &:disabled {
- background-color: var(--color-gray-100);
- }
- &:disabled {
- &::before {
- content: var(--tw-content);
- background-color: var(--color-gray-400);
- }
- }
- @media (forced-colors: active) {
- appearance: auto;
- }
- @media (forced-colors: active) {
- &::before {
- content: var(--tw-content);
- display: none;
- }
- }
-}
-.input-radio > label {
- display: block;
- color: var(--color-gray-800);
-}
-.select {
- cursor: pointer;
- border-radius: var(--radius-lg);
- background-color: var(--color-gray-100);
- padding: calc(var(--spacing) * 2);
- font-size: var(--text-sm);
- line-height: var(--tw-leading, var(--text-sm--line-height));
- color: var(--color-gray-900);
- &:focus {
- --tw-outline-style: none;
- outline-style: none;
- }
-}
-.input-file {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
-}
-.input-file > input {
- max-width: 100%;
- cursor: pointer;
- border-radius: var(--radius-lg);
- color: var(--color-gray-900);
-}
-.input-file > label {
- color: var(--color-gray-800);
-}
-.input-file > input::file-selector-button {
- cursor: pointer;
- background-color: var(--color-gray-50);
- padding: calc(var(--spacing) * 2);
- color: var(--color-gray-900);
-}
-.card {
- border-radius: var(--radius-xl);
- background-color: var(--color-white);
- background-clip: border-box;
- padding: calc(var(--spacing) * 4);
- --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
- box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
-}
-.collapse-button-icon {
- margin-block: auto;
- transform: rotate(0deg);
- transition: transform .2s linear;
-}
-.collapse-button-icon.open {
- transform: rotate(180deg);
- transition: transform .2s linear;
-}
-.dropdown {
- position: relative;
- display: inline-block;
- text-align: left;
-}
-.dropdown-body, .dropdown-body-start {
- position: absolute;
- left: calc(var(--spacing) * 0);
- z-index: 10;
-}
-.dropdown-body-end {
- position: absolute;
- right: calc(var(--spacing) * 0);
- z-index: 10;
-}
-.alert {
- display: flex;
- border-radius: var(--radius-lg);
- padding-inline: calc(var(--spacing) * 4);
- padding-block: calc(var(--spacing) * 4);
- color: var(--color-gray-700);
-}
-.tab {
- display: flex;
- cursor: pointer;
- border-bottom-style: var(--tw-border-style);
- border-bottom-width: 2px;
- border-color: var(--color-gray-300);
- padding-inline: calc(var(--spacing) * 6);
- padding-block: calc(var(--spacing) * 4);
- font-size: var(--text-base);
- line-height: var(--tw-leading, var(--text-base--line-height));
- --tw-font-weight: var(--font-weight-medium);
- font-weight: var(--font-weight-medium);
- color: var(--color-gray-500);
- &:hover {
- @media (hover: hover) {
- border-color: var(--color-gray-800);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-gray-800);
- }
- }
-}
-.tab-active {
- border-color: var(--color-primary-500);
- --tw-font-weight: var(--font-weight-bold);
- font-weight: var(--font-weight-bold);
- color: var(--color-primary-500);
- &:hover {
- @media (hover: hover) {
- border-color: var(--color-primary-600);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-primary-600);
- }
- }
-}
-.accordion {
- width: 100%;
-}
-.accordion > .accordion-item {
- width: 100%;
- padding-inline: calc(var(--spacing) * 4);
-}
-.accordion > .accordion-item > .accordion-button {
- display: flex;
- width: 100%;
- cursor: pointer;
- border-bottom-style: var(--tw-border-style);
- border-bottom-width: 2px;
- border-color: var(--color-gray-300);
- padding-inline: calc(var(--spacing) * 4);
- padding-block: calc(var(--spacing) * 4);
- font-size: var(--text-base);
- line-height: var(--tw-leading, var(--text-base--line-height));
- --tw-font-weight: var(--font-weight-medium);
- font-weight: var(--font-weight-medium);
- color: var(--color-gray-500);
- transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events;
- transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
- transition-duration: var(--tw-duration, var(--default-transition-duration));
- --tw-duration: 200ms;
- transition-duration: 200ms;
- &:hover {
- @media (hover: hover) {
- border-color: var(--color-gray-800);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-gray-800);
- }
- }
-}
-.accordion > .accordion-item > .accordion-button.accordion-button-active {
- border-color: var(--color-primary-500);
- --tw-font-weight: var(--font-weight-bold);
- font-weight: var(--font-weight-bold);
- color: var(--color-primary-500);
- &:hover {
- @media (hover: hover) {
- border-color: var(--color-primary-600);
- }
- }
- &:hover {
- @media (hover: hover) {
- color: var(--color-primary-600);
- }
- }
-}
-.accordion > .accordion-item > .accordion-button > .accordion-button-icon {
- margin-block: auto;
- margin-inline-start: auto;
- transform: rotate(0deg);
- transition: transform .2s linear;
-}
-.accordion > .accordion-item > .accordion-button > .accordion-button-icon.open {
- transform: rotate(180deg);
- transition: transform .2s linear;
-}
-.accordion > .accordion-item > .accordion-body {
- border-bottom-style: var(--tw-border-style);
- border-bottom-width: 2px;
- border-color: var(--color-gray-300);
- padding-inline: calc(var(--spacing) * 1);
- padding-block: calc(var(--spacing) * 4);
-}
-.accordion > .accordion-item:last-child > .accordion-body {
- border-bottom-style: var(--tw-border-style);
- border-bottom-width: 0px;
-}
-.table {
- table-layout: auto;
- display: table;
- width: 100%;
- :where(& > :not(:last-child)) {
- --tw-divide-y-reverse: 0;
- border-bottom-style: var(--tw-border-style);
- border-top-style: var(--tw-border-style);
- border-top-width: calc(1px * var(--tw-divide-y-reverse));
- border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
- }
- :where(& > :not(:last-child)) {
- border-color: var(--color-gray-200);
- }
- text-align: left;
- font-size: var(--text-sm);
- line-height: var(--tw-leading, var(--text-sm--line-height));
- white-space: nowrap;
-}
-.table:not(.table-stripped) > tbody {
- :where(& > :not(:last-child)) {
- --tw-divide-y-reverse: 0;
- border-bottom-style: var(--tw-border-style);
- border-top-style: var(--tw-border-style);
- border-top-width: calc(1px * var(--tw-divide-y-reverse));
- border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
- }
- :where(& > :not(:last-child)) {
- border-color: var(--color-gray-200);
- }
-}
-.table-striped > tbody > tr {
- &:nth-child(odd) {
- background-color: var(--color-white);
- }
- &:nth-child(even) {
- background-color: var(--color-gray-50);
- }
-}
-.table th {
- padding-inline: calc(var(--spacing) * 6);
- padding-block: calc(var(--spacing) * 3);
- font-size: var(--text-base);
- line-height: var(--tw-leading, var(--text-base--line-height));
- --tw-font-weight: var(--font-weight-bold);
- font-weight: var(--font-weight-bold);
- --tw-tracking: var(--tracking-wider);
- letter-spacing: var(--tracking-wider);
- color: var(--color-gray-800);
-}
-.table td {
- padding-inline: calc(var(--spacing) * 6);
- padding-block: calc(var(--spacing) * 4);
- font-size: var(--text-sm);
- line-height: var(--tw-leading, var(--text-sm--line-height));
- color: var(--color-gray-700);
-}
-.table > tbody > tr {
- --tw-duration: 100ms;
- transition-duration: 100ms;
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-gray-100);
- }
- }
-}
-.table-dense td {
- padding-inline: calc(var(--spacing) * 2);
- padding-block: calc(var(--spacing) * 1.5);
-}
-html, body {
- font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
-}
-h1:focus {
- outline: none;
-}
-a, .btn-link {
- color: #0071c1;
-}
-.btn-primary {
- color: #fff;
- background-color: #1b6ec2;
- border-color: #1861ac;
-}
-.content {
- padding-top: 1.1rem;
-}
-.valid.modified:not([type=checkbox]) {
- outline: 1px solid #26b050;
-}
-.invalid {
- outline: 1px solid red;
-}
-.validation-message {
- color: red;
-}
-#blazor-error-ui {
- background: lightyellow;
- bottom: 0;
- box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
- display: none;
- left: 0;
- padding: 0.6rem 1.25rem 0.7rem 1.25rem;
- position: fixed;
- width: 100%;
- z-index: 1000;
-}
-#blazor-error-ui .dismiss {
- cursor: pointer;
- position: absolute;
- right: 0.75rem;
- top: 0.5rem;
-}
-.blazor-error-boundary {
- background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
- padding: 1rem 1rem 1rem 3.7rem;
- color: white;
-}
-.blazor-error-boundary::after {
- content: "An error has occurred.";
-}
-@property --tw-rotate-x {
- syntax: "*";
- inherits: false;
-}
-@property --tw-rotate-y {
- syntax: "*";
- inherits: false;
-}
-@property --tw-rotate-z {
- syntax: "*";
- inherits: false;
-}
-@property --tw-skew-x {
- syntax: "*";
- inherits: false;
-}
-@property --tw-skew-y {
- syntax: "*";
- inherits: false;
-}
-@property --tw-border-style {
- syntax: "*";
- inherits: false;
- initial-value: solid;
-}
-@property --tw-font-weight {
- syntax: "*";
- inherits: false;
-}
-@property --tw-shadow {
- syntax: "*";
- inherits: false;
- initial-value: 0 0 #0000;
-}
-@property --tw-shadow-color {
- syntax: "*";
- inherits: false;
-}
-@property --tw-shadow-alpha {
- syntax: "
";
- inherits: false;
- initial-value: 100%;
+@property --tw-shadow-alpha {
+ syntax: "";
+ inherits: false;
+ initial-value: 100%;
}
@property --tw-inset-shadow {
syntax: "*";
@@ -2547,54 +1256,14 @@ a, .btn-link {
inherits: false;
initial-value: 0 0 #0000;
}
-@property --tw-duration {
- syntax: "*";
- inherits: false;
-}
-@property --tw-outline-style {
- syntax: "*";
- inherits: false;
- initial-value: solid;
-}
-@property --tw-translate-x {
- syntax: "*";
- inherits: false;
- initial-value: 0;
-}
-@property --tw-translate-y {
- syntax: "*";
- inherits: false;
- initial-value: 0;
-}
-@property --tw-translate-z {
- syntax: "*";
- inherits: false;
- initial-value: 0;
-}
-@property --tw-content {
- syntax: "*";
- initial-value: "";
- inherits: false;
-}
-@property --tw-divide-y-reverse {
- syntax: "*";
- inherits: false;
- initial-value: 0;
-}
-@property --tw-tracking {
- syntax: "*";
- inherits: false;
-}
@layer properties {
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
*, ::before, ::after, ::backdrop {
- --tw-rotate-x: initial;
- --tw-rotate-y: initial;
- --tw-rotate-z: initial;
- --tw-skew-x: initial;
- --tw-skew-y: initial;
+ --tw-space-y-reverse: 0;
--tw-border-style: solid;
+ --tw-leading: initial;
--tw-font-weight: initial;
+ --tw-tracking: initial;
--tw-shadow: 0 0 #0000;
--tw-shadow-color: initial;
--tw-shadow-alpha: 100%;
@@ -2609,14 +1278,6 @@ a, .btn-link {
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-offset-shadow: 0 0 #0000;
- --tw-duration: initial;
- --tw-outline-style: solid;
- --tw-translate-x: 0;
- --tw-translate-y: 0;
- --tw-translate-z: 0;
- --tw-content: "";
- --tw-divide-y-reverse: 0;
- --tw-tracking: initial;
}
}
}
diff --git a/src/sanbox/integration/ix-integration-blazor/wwwroot/js/theme.js b/src/sanbox/integration/ix-integration-blazor/wwwroot/js/theme.js
new file mode 100644
index 000000000..459ba97eb
--- /dev/null
+++ b/src/sanbox/integration/ix-integration-blazor/wwwroot/js/theme.js
@@ -0,0 +1,22 @@
+window.themeManager = {
+ init: () => {
+ if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
+ document.documentElement.setAttribute('data-theme', 'dark');
+ } else {
+ document.documentElement.setAttribute('data-theme', 'light');
+ }
+ },
+ setLight: () => {
+ localStorage.theme = 'light';
+ document.documentElement.setAttribute('data-theme', 'light');
+ },
+ setDark: () => {
+ localStorage.theme = 'dark';
+ document.documentElement.setAttribute('data-theme', 'dark');
+ },
+ setSystem: () => {
+ localStorage.removeItem('theme');
+ // re-run init to pick up OS setting
+ window.themeManager.init();
+ }
+};
diff --git a/src/sanbox/integration/ix-integration-plc/apax.yml b/src/sanbox/integration/ix-integration-plc/apax.yml
index 1632b9fda..003d21d5a 100644
--- a/src/sanbox/integration/ix-integration-plc/apax.yml
+++ b/src/sanbox/integration/ix-integration-plc/apax.yml
@@ -16,7 +16,7 @@ variables:
AXTARGET: 192.168.100.85
AXTARGETPLATFORMINPUT: .\bin\1500\
AX_USERNAME: "admin"
- AX_TARGET_PWD: "123ABCDabcd$#!"
+ AX_TARGET_PWD: "/op"
USE_PLC_SIM_ADVANCED: "false"
HWCONFIG: "HWC"
COM_CERT_PATH: .\certs\Communication.cer