Panel275 Documents

Generated policies

Dynamic Fonts (client)

Apply this policy to every client, including panels, sites, themes, and mobile apps, and all shared client components and their source, public, and asset directories, in development and production. No font file may exist anywhere inside client repositories, source trees, public or asset directories, or packaged application bundles. Fonts downloaded at runtime may be stored in browser-managed caches or an application cache on the device; these caches must never become source assets or bundled files. Never bundle, commit, copy, generate, or embed fonts in a client, including base64 or data URLs and icon fonts. Fonts must be served dynamically over HTTPS from the statics subdomain of the current tenant, resolved through tenant runtime configuration. Do not hardcode a tenant domain or load fonts directly from third-party font services or repository URLs. Before a font is used, its files must be committed and pushed to the holizm/fonts repository in the correct directory structure: use the established language group, or common only for fonts designed to support multiple languages, and the font-family directory, with camelCase directory and file names. Reuse an existing family directory and its naming conventions; place new families under <group>/<fontFamily>/<fontVariant>.<extension>. Font definition CSS files, including every @font-face rule and its source, weight, style, and display descriptors, must also reside in holizm/fonts alongside the relevant font family and be served from tenant statics. Never define @font-face rules or keep font definition stylesheets in a panel, site, theme, app, or shared client source. Clients may declare font-family usage and load the remote definition stylesheet through shared infrastructure. Deploy those repository files to the tenant's statics service while preserving their paths. Shared client infrastructure must resolve and load the configured font families, weights, styles, and URLs; panels, sites, themes, and mobile apps only declare short font names through shared infrastructure. Keep these declarations in their existing composition files; never create separate font path configuration files in clients. Font repository paths, language groups, definition stylesheet filenames, and asset URLs must remain inside core infrastructure and must not leak into themes or sites. Qwik clients use reusable SVG icons; do not include or load icon fonts unless a future customer requirement explicitly authorizes them. Mobile client infrastructure must download fonts from the configured tenant statics URLs and register them with the native runtime before use. Reuse downloaded fonts through the device application cache and use a system-font fallback while fonts are loading or unavailable.

Correct examples

Font repository: common/roboto/regular.woff2
Font repository: persian/bYekan/bYekan.woff2
Runtime font URL: https://statics.<tenantDomain>/fonts/common/roboto/regular.woff2

Incorrect examples

src: url('./assets/regular.woff2') format('woff2');
src: url('/themes/51/assets/inter400.ttf') format('truetype');
src: url('data:font/woff2;base64,...') format('woff2');
@import url('https://fonts.googleapis.com/css2?family=Roboto');

Fix

Commit and push font files to their correct family directory in holizm/fonts, move their font definition CSS files and @font-face rules into the same repository, publish both fonts and definition stylesheets through the tenant statics service, configure the resulting runtime font references, and remove every source or bundled font file and embedded font from all clients, including panels, sites, themes, and mobile apps. Use shared runtime font loading and native registration for mobile apps.

Dynamic Images (client)

Apply this policy to every panel, site, theme, and mobile app, including shared client components and their source, public, and asset directories. No image file may exist inside these clients. Every content image must be loaded dynamically from runtime content, API data, or configuration, with its URL or media identifier supplied by that data. Never bundle, commit, copy, or generate image assets inside a client, embed static images as base64 or data URLs, or hardcode image URLs in components or styles. This includes photographs, logos, backgrounds, placeholders, and icons. When a visual element is part of the design itself, implement it as a reusable SVG component whose colors and appearance use currentColor, theme tokens, or component properties; do not store it as a static image file. Use each platform's shared image component to render dynamic content images.

Correct examples

<Image image={content.hero.image} />
<Image source={{ uri: content.hero.image.url }} />
<BrandMark color={theme.colors.brand} />

Incorrect examples

import heroImage from './assets/hero.jpg'
<Image source={require('./assets/logo.png')} />
<img src='/themes/51/assets/hero.png' />
<img src='data:image/png;base64,...' />

Fix

Store content images in the media service or external media storage, persist their URLs or media identifiers in runtime content or configuration, and render them through the shared image component. Replace bundled design visuals with reusable, themeable SVG components.

No Show Identifier (client)

In panel, site, theme, and mobile app JSX files, never use an identifier named show. Use display when it supplies rendered content or formats a value, and use visible when it controls visibility. Keep externally required show names inside non-JSX interoperability adapters.

Correct examples

<Item display={display} />
<Dialog visible={visible} />

Incorrect examples

<Item show={display} />

Fix

Replace a rendering show identifier with display, replace a visibility show identifier with visible, and update its consumers. Isolate an externally required show property in a non-JSX adapter.

Color Variables (css)

Define every CSS color as a semantic custom property in the owning bundle’s single variables.css file and reference that property wherever the color is used. This includes text, backgrounds, borders, outlines, shadows, gradients, SVG colors, interaction states, and scrollbars. Literal color values may appear only in custom-property definitions in variables.css so site and panel themes can replace colors without changing component rules.

Correct examples

:root {
    --surfaceColor: #fff;
}

.item {
    background-color: var(--surfaceColor);
}

Incorrect examples

.card { background-color: #fff; color: rgb(20 20 20); }

Fix

Move the literal color into a semantic CSS custom property in the owning variables.css file and replace its usage with var(--propertyName).

Compound Classes (css)

In CSS for sites, themes, panels, and shared client components, separate independently meaningful concepts into class tokens instead of joining them into compound names. Use class="cards bento" instead of cardsBento and class="product details" instead of productDetails. Assign each class separately to the same element so the component or domain role and its layout, section, variant, or state can be composed independently. Define shared behavior on the base class and combination-specific differences with .cards.bento or nested &.bento, and .product.details or nested &.details. A space in markup joins classes on one element; .cards .bento in CSS selects a descendant and is not an equivalent replacement. This rule applies to ordinary CSS independently of Tailwind. Keep component names, filenames, imports, and other non-class identifiers camelCase.

Philosophy

Separate classes make composition possible without copying rules or creating a new compound name for every combination. The cards class supplies the shared collection structure while bento selects its arrangement; the same cards can take another layout and the same bento concept can be reused wherever that arrangement makes sense. The product class supplies product behavior while details identifies the detailed presentation, allowing other presentations to retain the same product foundation. Assigning these concepts independently lets a component change one concern while preserving the others. Shared rules belong to their owning role, and combined selectors express only the differences that require both concepts, avoiding accidental effects on unrelated elements.

Correct examples

Assign the concepts separately on the same element: class="cards bento" and class="product details".

.cards {
    display: grid;

    &.bento {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}
.product {
    display: grid;

    &.details {
        gap: 1rem;
    }
}

Incorrect examples

Avoid markup such as class="cardsBento" and class="productDetails".

.cardsBento {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
}
.productDetails {
    display: grid;
    gap: 1rem;
}

Fix

Replace compound class names with separate semantic tokens in markup, CSS selectors, scripts, and tests. Keep the base role on each variant, reuse each token where its meaning applies, and scope combination-specific styles with combined selectors or nested & selectors.

Declaration Lines (css)

Put each @apply directive and each CSS declaration on its own line.

Correct examples

.item {
    @apply border;
    background: var(--surfaceColor);
}

Incorrect examples

.card {
    @apply border; color: var(--textColor);
}

Fix

Place every @apply and CSS property on a separate line between the selector braces.

Logical Direction (css)

Use logical start and end instead of physical left and right in CSS. This applies to properties, values, and directional utility classes, including utilities used through @apply.

Philosophy

Left and right describe fixed physical sides. Start and end follow the writing direction, so the same styles work in both LTR and RTL layouts.

Correct examples

.card {
    margin-inline-start: 1rem;
    padding-inline-end: 1rem;
    text-align: start;
}
.action {
    inset-inline-end: 0;
}

Incorrect examples

.card {
    margin-left: 1rem;
    padding-right: 1rem;
    text-align: left;
}
.action {
    right: 0;
}

Fix

Replace physical left and right with the corresponding logical inline-start and inline-end properties, values, or utilities. Verify the result in both LTR and RTL.

Nested Component Selectors (css)

Nest descendant, state, and pseudo-element rules inside their owning selectors instead of repeating flat selector paths.

Correct examples

.footer {
    .cta {
        &:hover {
            color: var(--interactionColor);
        }
    }
}

Incorrect examples

.footer .cta:hover {
    color: var(--interactionColor);
}

Fix

Move the descendant or state rule inside its owning selector and use & for states and pseudo-elements.

No Direct Utility Import (css)

Do not import or reference Tailwind directly in component, part, site, panel, or theme CSS. This includes @import and @reference directives targeting tailwindcss or its package subpaths. Load Tailwind once through the shared core global style entry; component styles may use @apply without importing or referencing Tailwind again.

Correct examples

@import './parts/media.css';
.virtualTour {
    @apply my-8 w-full;
}

Incorrect examples

@reference 'tailwindcss';
@import 'tailwindcss';
@import 'tailwindcss/utilities';

Exceptions

  • Site and panel core shared global style entry points may load Tailwind.

Fix

Remove the direct Tailwind @import or @reference and use the existing shared core global style entry.

Prefer Tailwind Utilities (css)

Try to use Tailwind utilities in CSS files instead of writing pure CSS.

Philosophy

Usually this causes lower lines of code and fewer bytes per file.

Correct examples

.links {
    @apply flex flex-wrap gap-4;
}
.icon {
    @apply size-5 fill-current;
}

Incorrect examples

.links {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
.icon {
    fill: currentColor;
    height: 1.25rem;
    width: 1.25rem;
}

Exceptions

  • The enforcer checks only a conservative set of direct equivalents. It skips theme-dependent values, important declarations, ambiguous same-block overrides, blocks containing directives, and definition or keyframe contexts.

Fix

Use @apply with equivalent Tailwind utilities where practical, preserving existing behavior and semantic theme variables.

Property Order (css)

Sort CSS property declarations alphabetically within each selector. Keep @apply directives separate from the property order.

Correct examples

.item {
    @apply border;
    background: var(--surfaceColor);
    border-color: var(--lineColor);
}

Incorrect examples

.card {
    border-color: var(--lineColor);
    background: var(--surfaceColor);
}

Exceptions

  • Preserve source order when a shorthand and one of its longhands intentionally override each other.

Fix

Reorder CSS properties alphabetically without changing the order of repeated fallback values for one property.

Root Variables (css)

Define every CSS custom property in one variables.css file for each CSS bundle. Put default values at :root and theme overrides at the owning root selector in that same file. Component stylesheets must only reference those variables; they must not define variables, even in their own :root block. Keep Tailwind design tokens and permitted theme-root utility variables in variables.css too, so all values for a new theme can be found in one place.

Correct examples

variables.css:

:root {
    --surfaceColor: #fff;
}

:root.dark {
    --surfaceColor: #111;
}

Incorrect examples

components/browse.css:

:root {
    --browseTitleSurface: #fff;
}

.browse {
    .title {
        background: var(--browseTitleSurface);
    }
}

Fix

Move every custom-property definition into the owning bundle’s variables.css, group defaults and theme overrides by root selector, and leave only var(...) references in component stylesheets.

Selector Lines (css)

Put each selector in a group on its own line, with the opening brace after the last selector. Put declarations and nested selectors below the brace.

Correct examples

.item {
    @apply border;
}
.item,
.field {
    @apply border;
}

Incorrect examples

.card { @apply border; }
.card, .tile {
    @apply border;
}

Fix

Split grouped selectors at commas and move declarations and nested selectors below the opening brace.

Selector Spacing (css)

Leave one empty line between sibling CSS selectors, including nested selectors.

Correct examples

.item {
    @apply border;
}

.title {
    @apply text-xl;
}

Incorrect examples

.card {
    @apply border;
}
.title {
    @apply text-xl;
}

Fix

Insert one blank line after a selector block before the next sibling selector.

Semantic Color Variable Names (css)

In the owning bundle’s single variables.css file, choose each color variable name from its UI purpose, not its current value. Start with the narrowest meaningful element or semantic role, then add the colored property or interaction state when needed to distinguish it, such as --cardSurface, --cardBorderHover, or --footerHeadingText. If several elements use a color for the same purpose, name their shared role rather than the first element that used it; if their purposes differ, give them separate variables even when their current values match. A variable name must remain accurate at every place it is used. Avoid color words, palette shades, and numeric scales, such as green, darkBlue, or gray100, including in a one-page or one-tenant theme.

Correct examples

:root {
    --cardSurface: #fff;
    --cardBorderHover: #748f55;
    --ctaText: #fff;
}

.card {
    background-color: var(--cardSurface);

    &:hover {
        border-color: var(--cardBorderHover);
    }
}

.cta {
    color: var(--ctaText);
}
:root {
    --textOnDarkSurface: #fff;
    --footerHeadingText: #fff;
}

.hero .title,
.dialog .title {
    color: var(--textOnDarkSurface);
}

.footer .title {
    color: var(--footerHeadingText);
}

Incorrect examples

:root {
    --about-green: #748f55;
    --darkBlue: #1e3a8a;
    --gray100: #f3f4f6;
}
:root {
    --footerHeadingText: #fff;
}

.hero .title {
    color: var(--footerHeadingText);
}

Fix

Inspect every use of the variable, identify the common UI purpose or separate purposes, and name each variable accordingly. For example, replace --about-green with --aboutCardAccent, and do not use --footerHeadingText for a hero title.

Atomic Ui (jsx)

Build JSX as a recursive atomic hierarchy. Put reusable panel UI components in panel core and reusable domain site UI in part sites. Put site- or theme-specific UI and SVGs in their owning site or theme, never in site core. Compose leaf interfaces such as blog pages, course pages, lists, and forms from the appropriate components. Keep each layer focused so UI can be reused without moving visual elements into site core.

Boolean Attribute (jsx)

Pass a JSX boolean prop by its presence, such as someAttribute, instead of someAttribute={true}.

Correct examples

<Component someAttribute />

Incorrect examples

<Component someAttribute={true} />

Fix

Remove ={true} from boolean JSX attributes and keep the attribute name.

Composable Ui (jsx)

Make every JSX layer composable and reusable. Compose core atoms into larger part components, and compose part components into leaf interfaces without duplicating JSX for different sites, themes, panels, or panel themes. Use stable semantic component boundaries and canonical prop contracts so a component can be reused or replaced without replacing its parent, page, part, or bundle. Define shared compositions once and resolve configured implementations through progressively broader fallbacks when configuration is supported.

Concise Component (jsx)

When an arrow component body contains only a return statement returning JSX, return the JSX directly with a concise arrow function. Keep a block body when it contains additional statements, such as hooks, variable declarations, or conditional returns.

Correct examples

export default props => <Lookup {...props} />
export default props => <>
    <Title {...props} />
    <Content {...props} />
</>
export default props => {
    const value = useValue()
    return <Field value={value} />
}

Incorrect examples

export default props => {
    return <Lookup {...props} />
}
export default props => {
    return <>
        <Title {...props} />
        <Content {...props} />
    </>
}

Fix

Remove the block braces and return keyword, leaving the JSX directly after => without wrapping parentheses. Preserve existing comments.

Conditional Rendering Ownership (jsx)

Move conditional rendering controlled by a component-specific hasX prop into that component. Parent and intermediate components must render the child unconditionally and forward props unchanged; the controlled component must consume hasX and return null when it is false. Keep a condition in a parent only when it controls broader composition, layout, or coordination among multiple children.

Correct examples

export default props => <Items {...props} />
export default props => {
    if (!props.hasItems) return null
    return <ul />
}
export default props => <main>
    {
        props.hasDashboard && <>
            <Chart {...props} />
            <Legend {...props} />
        </>
    }
</main>

Incorrect examples

export default props => <main>
    {
        props.hasItems && <Items {...props} />
    }
</main>

Fix

Move the hasX guard into the controlled child. Conditions coordinating multiple children remain in the parent. Review ambiguous single-child layout guards manually.

Continuous Opening Tag (jsx)

Do not leave an empty line inside a multiline HTML or JSX opening tag.

Correct examples

<Component
    first={first}
    second={second}
/>

Incorrect examples

<Component
    first={first}

    second={second}
/>

Fix

Remove empty lines from the multiline opening tag.

Fragment Child Lines (jsx)

Place empty fragment tags on lines separate from their children. Put <>, every child, and </> on separate lines so fragment boundaries and sibling elements remain explicit.

Correct examples

<>
    <Title />
    <Content />
</>

Incorrect examples

<><Title /><Content /></>

Fix

Move the empty fragment opening and closing tags onto lines separate from their children while preserving the children and existing comments.

Headless Ui (jsx)

Keep the atomic JSX system headless. Components may define semantic structure, JavaScript behavior, state, and accessibility, but must not define UI appearance or look and feel. Do not put Tailwind utility classes or visual inline style declarations in JSX, including class fallbacks and class values passed through props. Expose semantic role and domain class names and implement visual decisions in the applicable site or panel styles.

Correct examples

export default props => <div className='item'>
    <Heading {...props} />
    <Content {...props} />
</div>
<Item class='video' />

Incorrect examples

export default props => <section style={{ color: 'red' }}>
    <Content {...props} />
</section>
<Item class='video w-full my-8' />

Exceptions

  • Write Tailwind sizing classes directly in Image.containerClass because the component reads them to calculate srcset.

Fix

Remove visual styling from JSX and implement it against semantic class names in theme CSS.

Jsx Attribute Formatting (jsx)

Keep an HTML or JSX opening tag with exactly one attribute on one line. When an HTML or JSX opening tag has two or more attributes, use a multiline opening tag and place every attribute on its own line. Always sort multi-lined attributes in JSX and HTML.

Correct examples

<Field value={value} />
<Field
    disabled
    value={value}
/>
<Field
    {...rest}
    disabled
    value={value}
/>

Incorrect examples

<Field disabled value={value} />
<Field
    value={value}
/>
<Field
    value={value}
    disabled
/>

Fix

Use one line for one attribute; otherwise give each attribute its own line and sort named attributes within each spread boundary. Preserve spread order because it controls overrides.

Jsx Expression Braces (jsx)

Place the opening and closing curly braces of JSX child expression containers on separate isolated lines. A single-line <td> containing only property access, including optional chaining, may keep its expression inline. For every multiline JavaScript expression inside JSX curly braces, including attribute values, place the expression on lines separate from both braces. Single-line attribute expressions may remain beside their braces.

Correct examples

export default item => <td>{item?.firstName}</td>
export default ({ title }) => <h1>
    {
        title
    }
</h1>
<Field
    disabled
    value={
        ready
            ?
            'yes'
            :
            'no'
    }
/>

Incorrect examples

export default ({ title }) => <h1>{title}</h1>
<Field
    disabled
    value={ready
        ?
        'yes'
        :
        'no'
    }
/>
<Field
    disabled
    value={
        ready
            ?
            'yes'
            :
            'no'}
/>

Exceptions

  • A single-line <td> element may keep inline braces when its only content is property access, including optional chaining. Calls, operators, additional content, and multiline cells do not qualify.

Fix

Put child-expression braces on their own lines, except in single-line <td> elements containing only property access. In multiline attribute expressions, put the opening brace after the attribute name, start the expression on the next line, and put the closing brace on a separate line after the expression.

Jsx Ternary Formatting (jsx)

Always format a ternary expression across exactly five parts on separate lines: the condition, ?, the true expression, :, and the false expression.

Correct examples

export default props => props.visible
    ?
    <Content />
    :
    null

Incorrect examples

export default props => props.visible ? <Content /> : null

Fix

Place the condition, question mark, true branch, colon, and false branch on separate lines. JSX branches may span multiple lines.

Light Qwik Component (jsx)

When a Qwik component does not require component$, convert it to a light component by removing component$ and any associated boilerplate.

Correct examples

import { component$ } from '@builder.io/qwik'
export default component$(() => {
    const count = useSignal(0)
    return <span>
        {
            count.value
        }
    </span>
})
export default props => <Content {...props} />

Incorrect examples

import { component$ } from '@builder.io/qwik'
export default component$(props => <Content {...props} />)

Fix

Remove component$ around presentation-only components. Keep boundaries using hooks, events, or Qwik slots; unknown calls are conservatively retained for review.

Nonempty File (jsx)

A JSX file must contain a meaningful minimal implementation. Empty, whitespace-only, and comment-only JSX files are not allowed.

Correct examples

export default props => <Content {...props} />

Incorrect examples


   

// Placeholder

Fix

Implement the intended component or module with the smallest useful code, or delete the unused file and its references.

One Component Per File (jsx)

Keep one primary JSX component in each file, including React and Qwik components.

Correct examples

export default props => <Content {...props} />

Incorrect examples

export const Header = props => <header {...props} />
export default props => <footer {...props} />

Fix

Move each additional JSX component into its own file.

Prop Destructuring Ownership (jsx)

Destructure props only in components that consume them. This usually means leaf components destructure props, although intermediate components may do so when they consume specific prop values themselves.

Correct examples

export default props => <Card {...props} />
export default ({ title }) => <h1>
    {
        title
    }
</h1>

Incorrect examples

export default ({ title }) => <Card title={title} />

Fix

Forward the original props object; destructure values only when consumed locally. This check detects parameter bindings used only as component attributes.

Semantic Class Names (jsx)

Name platform-owned JSX classes after the element's meaning or structural role, never its appearance, layout, or theme.

Correct examples

<footer className='footer'>
    <section className='cta' />
</footer>

Incorrect examples

<section className='blueBox' />

Fix

Replace the visual class name with one that describes the element's meaning or structural role.

Shared Ui Behavior (jsx)

Before creating a JSX component for an interaction, search the relevant core and part components for existing behavior and an accessibility contract. Reuse matching behavior. If no matching implementation exists and the interaction is domain-neutral, define its state and event handling once in headless core infrastructure, even when the first use is in one part or page. Site core may render reusable domain-neutral controls as headless JSX and semantic HTML. Put domain controls in the appropriate part site and site- or theme-specific controls in their owner. Core and part-site components must leave appearance styles to the consuming site or theme. Panel core may own reusable panel controls. Pass domain data and localized labels through generic props. A reusable clipboard action and its headless copy button may live in site core, with localized labels supplied through props and appearance supplied by the consuming site or theme.

Fix

Reuse matching interaction behavior or define missing domain-neutral behavior in headless core infrastructure. Keep rendered site controls in their owning site, theme, or part site.

Single Child Fragment (jsx)

When JSX contains only one element or expression, render it directly instead of wrapping it in an empty fragment. Use an empty fragment only to group multiple siblings.

Correct examples

<>
    <Title />
    <Content />
</>
<Field />

Incorrect examples

<>
    <Field />
</>

Fix

Remove the empty fragment around the single child while preserving the child and existing comments.

Static Jsx Export (jsx)

In panel and site code, export JSX directly when the default arrow has no parameters and its JSX contains no expressions or spreads. Panel builds wrap the value as a component; Qwik site code can render the value directly.

Correct examples

export default <div className='item'>
    <Title />
</div>
export default <ForcedVerticalScroll />
export default props => <Content {...props} />

Incorrect examples

export default () => <div className='item'>
    <Title />
</div>
export default () => <ForcedVerticalScroll />

Fix

Replace the parameterless arrow with its JSX body. In site code, update consumers to render the exported JSX value directly. Keep a function when props, hooks, or render-time values are needed.

Unparenthesized Returned Jsx (jsx)

Never wrap returned JSX in parentheses.

Correct examples

export default props => <Content {...props} />

Incorrect examples

export default props => (<Content {...props} />)

Fix

Remove parentheses around returned JSX while preserving comments.

Action Failure Feedback (panel)

When a panel action fails, show a localized failure message through shared panel feedback. Logging an error or silently returning is not sufficient user feedback. Keep the message free of provider response bodies, credentials, and personal data.

Fix

Catch the action failure and call the shared localized error feedback mechanism with an existing translation key or a properly defined new key.

Core Behavior Ownership (panel)

Panel core owns shared behavior and its contracts: authentication, routing, data access, initialization, accessibility semantics and state, keyboard and focus handling, reusable interaction state and events, browser-preference handling, and dark/light behavior. Custom panels, including headless runnables, remain thin: they declare configuration, data, and composition and supply presentation markup and styles. They consume core behavior rather than duplicating it or manually maintaining shared accessibility attributes and interaction state. Expose declarative choices through core contracts, keep visual decisions in the custom panel, and use existing core elements when they can own the behavior without an extra wrapper.

Philosophy

A custom design should change presentation, not require reimplementing accessibility or interaction behavior.

Correct examples

Panel core owns the initial busy state on the existing root element; the custom panel supplies loader visuals and styles.
A custom panel declares panel.onlyLightMode; core applies and maintains the appearance mode.

Fix

Move shared behavior and accessibility management into panel core. Have the custom panel declare its requirements and render and style the resulting presentation through the core contract.

Core Rich Text (panel)

Do not directly use ReactMarkdown or other rich text renderers or editors in part panels, runnable panels, or application components. Reuse rich text display components and editor fields from panel core. Third-party rendering and editor integrations belong only inside their owning shared core components. Core previews must also reuse the shared rich text display component.

Philosophy

Panel core owns rich text rendering and editing so applications share consistent behavior, appearance, accessibility, and security.

Correct examples

import { RichText } from 'panel'
<RichText content={post.content} format='markdown' />
import { Markdown } from 'form'

Incorrect examples

import ReactMarkdown from 'react-markdown'
import { EditorContent } from '@tiptap/react'

Fix

Move rendering or editing mechanisms into the owning shared panel core component. Import RichText from panel for display and reuse the shared form fields for editing. Keep rendering configuration and appearance in core; callers declare their content and format.

Form Parts (panel)

Compose forms from fields, actions, help text, form-level status, and form-level validation. Each field consists of a label, a control, help text or a tip, validation status and messages, and icons.

Future Theme Boundary (panel)

Design the architecture to support future panel theming, primarily for colors and spacing, but do not implement theming until explicitly requested.

Import Order (panel)

In panel code, order imports from React and third-party libraries through core, contexts, hooks, and components to local modules.

Correct examples

import React from 'react'
import Button from '@mui/material/Button'
import core from '../../core/module.js'
import context from '../contexts/context.js'
import hook from '../hooks/hook.js'
import component from '../components/component.jsx'

Incorrect examples

import component from '../components/component.jsx'
import React from 'react'

Fix

Move imports into the required layer order.

List File Boundaries (panel)

A panel list.jsx composes the list only. Define its list actions, filters, headers, row, card, form, fields, field, filter, and item in sibling files named listActions.jsx, filters.jsx, headers.jsx, row.jsx, card.jsx, form.jsx, fields.jsx, field.jsx, filter.jsx, and item.jsx. Do not define these concerns inside list.jsx.

Correct examples

import { List } from 'list'; export default props => <List {...props} />
import Form from './form'; export default props => <List upsert={Form} {...props} />

Incorrect examples

const Row = props => <tr><td>{props.name}</td></tr>; export default props => <List row={Row} {...props} />
export default props => <List filters={[{ name: 'status' }]} {...props} />
export default props => <form><input name='search' /></form>

Fix

Move each inline list concern into its own sibling file, then let the list resolver or an import compose it.

List Part Convention (panel)

Let resolveListParts.js resolve conventional sibling list parts. Importing a supported sibling into list.jsx and passing it unchanged to the matching List prop creates unnecessary boilerplate.

Correct examples

export default props => <List {...props} />
import row from './row'; export default props => <List row={props.row || row} />

Incorrect examples

import row from './row'; export default props => <List row={row} />
import Row from './Row.jsx'; export default props => <List ROW={Row} />
import Actions from './itemActions'; export default props => <List itemActions={Actions} />

Fix

Remove the sibling list-part import and matching prop. The panel list resolver supplies it by convention.

List Parts (panel)

Compose lists from list actions, list parameters, filters, items, pagination, and sorting. Present items as cards, table rows, hierarchical forests or trees, maps, or graphs, as appropriate. Each item must also provide item-specific actions.

List Search (panel)

Panel lists must use their single list search for free-text matching. A filters.jsx file may contain only structured constraints such as enum, boolean, numeric, date, or relation filters; it must not render Key, Search, Text, Title, or an equivalent free-text control. Read search availability as searchable from useApp rather than defining, passing, or consuming hasSearch. Delete filters.jsx when no structured filters remain.

Correct examples

import { Boolean } from 'list'; export default <Boolean property='active' />
const { searchable } = useApp()

Incorrect examples

import { Title } from 'list'; export default <Title />
const { hasSearch } = props

Fix

Remove free-text controls from filters.jsx, remove every hasSearch contract, and read searchable from useApp where search availability is needed. Delete filters.jsx if no structured filters remain.

Localized Panel Text (panel)

Store panel labels, titles, tooltips, validation messages, and action text in localization resources.

Native Form Submission (panel)

Use the platform form submission flow instead of replacing it with ad hoc click handlers.

Own Part Common Import (panel)

In panel code, import the current part’s common modules only through its generated ${part}Common alias. Do not use relative paths into a common directory. A part must not import another part’s common alias or internal common modules.

Correct examples

Importing file: /home/dev/contacts/panel/admin/jobTitle/list.jsx

import row from 'contactsCommon'
import { findCategory } from 'taxonomy'

Incorrect examples

Importing file: /home/dev/contacts/panel/admin/jobTitle/list.jsx

import row from '../../common/textRow'
import row from 'accountsCommon'

Fix

Use the current part’s generated ${part}Common alias, or import another part through its public module.

Panel Architecture (panel)

The panel repository owns shared client behavior, including authentication, routing, data access, and reusable headless mechanisms. The complexPanel repository supplies the ready-made complex UI for admin panels. The simplePanel repository supplies a reusable simpler UI. A headless runnable owns its distinct UI and styles while composing shared panel behavior and suitable headless components. Keep design choices out of shared core behavior.

Philosophy

Reuse behavior independently of the design that presents it.

Fix

Place shared mechanisms in panel core and presentation in the selected UI repository or owning headless runnable.

Prop Forwarding (panel)

In components that both consume and forward props, destructure only the props consumed by that component and collect all remaining props in ...rest. If only part of the props object can be forwarded unchanged, pass the remaining rest object directly to child components, even when they receive unused props. When different children consume different forwarded props, pass the same rest object to each child. Whenever possible, pass the entire props object instead of creating a rest object. Intermediate components must not rename, alias, derive, reshape, or individually remap props solely for forwarding. Pass the original props object unchanged. Align the producer and consuming leaf component on a single canonical prop contract instead. An intermediate component may construct a prop only when it originates or consumes the value itself.

Public Core Boundary (panel)

Parts, runnables, and runnable parts must access headless panel core through its public panel module, or panelTests for test helpers. Keep test exports in the separate panelTests barrel, never in production panel barrels. Never reach into panel core files through relative paths, absolute paths, file URLs, or deep panel imports. This includes tests, fixtures, re-exports, dynamic imports, and filesystem access. Internal file access is permitted inside panel core itself.

Philosophy

Consumers depend on stable public contracts rather than the core repository layout.

Correct examples

import { List } from 'panel'
import row from './row.jsx'

Incorrect examples

import verify from '../../../../../panel/test/verifyRecordViewTests.js'
export { List } from 'panel/components/list.jsx'

Fix

Use panel for runtime functionality and panelTests for test helpers. If a required capability is missing, expose it through the appropriate supported core interface instead of importing an internal file.

Separate Component Concerns (panel)

In panel development, keep distinct concerns in separate component files whenever applicable. For example, use list.jsx, listActions.jsx, filters.jsx, sorts.jsx, headers.jsx, row.jsx, card.jsx, itemActions.jsx, form.jsx, fields.jsx, field.jsx, filter.jsx, and item.jsx instead of combining them into larger components.

Shared Image (panel)

Render every image in panel core, part panels, and runnable panels through the shared Image component from panel core. Never write an img JSX tag in panel source, including inside the shared component. The shared component alone creates the native image element.

Correct examples

import { Image } from 'panel'; export default ({ item }) => <Image alt={item.title} source={item.imageUrl} />
import Image from '../components/image'; export default ({ url }) => <Image source={url} />

Incorrect examples

export default ({ item }) => <img alt={item.title} src={item.imageUrl} />
const preview = <IMG src={url} />

Fix

Import Image from panel in parts and runnables, or from the shared component in panel core. Pass the image URL as source and preserve alt, className, and other relevant props.

Shared Panel Routing (panel)

Every panel UI must reuse the routing engine from panel core, including complexPanel, simplePanel, and headless panels. Core owns route collection, permissions, query handling, and router execution. The consuming UI supplies layouts, loading and not-found views, component presentation, and special-page composition. Do not create a second routing engine in a UI repository or runnable.

Philosophy

A different panel design must not require a separate routing implementation.

Fix

Compose the shared core router and supply UI-specific presentation through its supported composition interface.

Simple Html (panel)

In the general panel, use the simplest HTML elements. Use div and span for structure and text, a for links, and native controls for interaction. Do not use semantic page or content elements such as main, section, article, header, footer, nav, or aside. This policy does not apply to part panels.

Fix

Replace semantic page and content elements in the general panel with simple HTML elements while preserving links, controls, and accessibility behavior.

Trusted Markup (panel)

Render data-supplied HTML and SVG in panels only through the panel core SafeMarkup boundary. Select html or svg mode explicitly for SVG. Do not assign raw markup to innerHTML, use dangerouslySetInnerHTML, or insert adjacent HTML in part panels, panel core, or runnable panels.

Correct examples

import { SafeMarkup } from 'panel'; export default ({ value }) => <SafeMarkup html={value} mode='svg' />

Incorrect examples

export default ({ value }) => <span dangerouslySetInnerHTML={{ __html: value }} />
element.innerHTML = value

Fix

Replace the raw markup sink with SafeMarkup from panel core. For imperative panel core rendering, sanitize immediately before the DOM write through the same sanitizer.

Window Global (react)

Do not access the window global in React code. Use framework hooks, contexts, injected dependencies, or focused adapters so components remain testable and independent of browser-global state.

Correct examples

const location = useLocation()
const [searchParams] = useSearchParams()

Incorrect examples

const searchParams = new URLSearchParams(window.location.search)

Fix

Replace window access with the applicable React or router abstraction. Isolate an unavoidable browser API behind a focused adapter and inject it where it is consumed.

Class Name Casing (tailwind)

Use camelCase for every platform-owned custom class name in Tailwind component styles.

Correct examples

.someClass {
    @apply block;
}

Incorrect examples

.some-class { @apply block; }

Fix

Rename kebab-case and snake-case custom classes to camelCase in styles and every matching JSX reference.

Class Name Namespace (tailwind)

Aim for single-word classes as much as possible. Separate a component role and its declared variant into class tokens on the same element: use class="hero minimal" and class="hero split", never heroMinimal or heroSplit as class names. Every variant keeps the base role so shared styles apply. Style the base with .hero and variant differences with nested &.minimal or the compound selector .hero.minimal, never the descendant selector .hero .minimal. Apply this to shared components, sites, and every theme, updating markup, selectors, and class-dependent behavior together. Keep component names, filenames, imports, and content renderer identifiers camelCase; they are not CSS class tokens. Do not mechanically split a compound word that names one cohesive semantic role. Let selector nesting express ownership instead of repeating the parent name in every child class. If sibling classes have the same prefix, that prefix may be their missing parent: move it up one level and name the children by their own roles. For example, browseDialogTitle and browseDialogContent indicate a shared browse parent with .title and .content children.

Correct examples

.footer {
    .cta {
        @apply block;
    }
}
.browse {
    .title {
        @apply font-semibold;
    }

    .content {
        @apply block;
    }
}
.hero {
    @apply block;

    &.minimal {
        @apply text-center;
    }
}

Incorrect examples

.footer {
    .footerCta { @apply block; }
}
.browseDialogTitle {
    @apply font-semibold;
}

.browseDialogContent {
    @apply block;
}

Fix

Separate component and variant class tokens on the same element, retain the base role on every variant, and update all matching selectors and class-dependent behavior across sites and themes. For descendant ownership, add the shared parent class, nest sibling selectors under it, and shorten child classes to their roles. Preserve unrelated semantic compound names and non-class identifiers.

Always Visible Vertical Scrollbar (ui)

Reserve vertical scrollbar space anywhere its appearance or disappearance would cause UI layout shift. Use overflow-y-scroll for primary page and panel scrolling regions, including every website root, or scrollbar-gutter: stable when the scrollbar itself should remain conditional.

Correct examples

.panelContent {
    @apply overflow-y-scroll;
}
html {
    overflow-y: scroll;
}
html {
    scrollbar-gutter: stable;
}

Incorrect examples

.panelContent {
    @apply overflow-y-auto;
}
html {
    overflow-y: auto;
}

Exceptions

  • Print styles may allow the full document to flow without reserving a screen scrollbar.

Fix

Reserve vertical scrollbar space with overflow-y-scroll or scrollbar-gutter: stable on the primary scrolling region.

Development Panel Client (part/partPanel)

Every panel running in development must authenticate through the IAM client named panel, regardless of its role or runnable process name. Do not declare accounts.client in a panel settingsOverride.json; let shared panel infrastructure select the client for the environment and role.

Philosophy

All development panels share one IAM client while their roles continue to control authorization.

Correct examples

{}

Incorrect examples

{ "accounts": { "client": "adminPanel" } }
{ "accounts": { "client": "userPanel" } }
{ "accounts": { "client": "panel" } }

Fix

Remove accounts.client from the panel settingsOverride.json and let shared panel infrastructure select the IAM client.

File System Hierarchy (part/partPanel)

Structure each part panel directory with common and admin directories.

Panel Route Shape (part/partPanel)

Panel menu destinations and route declarations must use /${part}/${type}/${action} for type actions or /${part}/${action} for part actions. A bare /${part} path is invalid in panels because the action must be explicit. These segments identify the owning part, optional type, and action; they are a semantic contract used by shared panel infrastructure to resolve route context and data. Apply the same contract to complex, simple, and headless panels. Menu destinations must match the corresponding route declarations and preserve their meaning. Do not substitute display labels, layout names, or arbitrary page aliases for part, type, or action segments. Keep route-context extraction in shared core infrastructure rather than duplicating path parsing in custom panels. Preserve the application home at / as a separate application-level route.

Philosophy

Paths describe application meaning consistently across APIs, panels, and sites; panel designs must preserve that meaning.

Correct examples

/calls/call/list
/accounts/user/find
/settings/applicationSettings

Incorrect examples

/calls
/myCalls
/dashboard/calls
/call/list
/calls/call/history/list

Fix

Align menu destinations and route declarations with the owning part, optional type, and action. Reuse shared route-context extraction in core.

Root Panel Declarations (part/partPanel)

Keep menu data in menu.jsx, route declarations in routes.jsx, and app-action composition in appActions.jsx at the runnable panel root. Apply this convention to every panel UI. Files under panel may implement rendering components, but must not duplicate these root declarations. A root menu.jsx declares data while panel/menu.jsx may render that data.

Philosophy

Separate application declarations from the components that present them.

Correct examples

userPanel/menu.jsx: menu data
userPanel/routes.jsx: route declarations
userPanel/appActions.jsx: app-action composition
userPanel/panel/menu.jsx: menu renderer

Fix

Move declaration data to the runnable root and make UI components consume it rather than maintaining a second copy.

Dashboard Composition (part/partPanel/complex)

In complex panels, always render the dashboard with DashboardsDashboardDashboard imported from dashboardsCommon.

Philosophy

Reuse the shared dashboard component so dashboard behavior and translation run through the established panel composition.

Fix

Import DashboardsDashboardDashboard from dashboardsCommon and use it as the dashboard component in the route declaration.