Building admin screens
The core provides all of the styling for admin screens. Module developers only need to write markup that follows the conventions to get the same look as the core admin, and when the core changes its design, module screens follow along.
Basic principles
- Colors, spacing, and fonts on admin screens are set by the core's design tokens (
--zm-*CSS variables). Don't hard-code color values in your module; reference the tokens. - Keep using the
x_*classes inherited from the old XE days. The core kept the classes and only applied new styles to them, so existing modules get the new design without any changes. - If your module needs its own UI, create a prefix just for that module. Overriding core classes will break in the next core update.
Design tokens
These CSS variables are available across all admin screens. Both light and dark values are defined, so dark mode works automatically as long as you use the tokens.
| Token | Purpose |
|---|---|
--zm-brand --zm-brand-dark --zm-brand-soft | Accent color (default #2677e3), darker variant, light background |
--zm-ink --zm-ink-soft --zm-ink-faint | Body text, secondary text, faint text |
--zm-bg --zm-card --zm-surface --zm-surface-2 | Page background, card, surface levels |
--zm-line --zm-line-soft --zm-input-border | Divider, light divider, input border |
--zm-ok --zm-warn --zm-error | Status colors (success/warning/error) |
--zm-radius --zm-radius-sm | Corner radius (12px / 8px) |
--zm-hover --zm-solid --zm-solid-fg | Hover background, emphasized button fill and text |
--zm-shadow --zm-font | Card shadow, font stack |
/* 모듈 관리자 화면 예 */
.mymod-note {
padding: 12px 16px;
border: 1px solid var(--zm-line);
border-radius: var(--zm-radius-sm);
background: var(--zm-surface-2);
color: var(--zm-ink-soft);
}x_* components
These are the basic building blocks of admin markup. Combine the classes below to get the same screens as the core without any extra CSS.
- Forms:
.x_form-horizontal>.x_control-group>.x_control-label+.x_controls - Buttons:
.x_btn, emphasized.x_btn.x_btn-primary, dangerous actions.x_btn-danger, sizes-large-small-mini - Tables:
.x_table,.x_table-stripedfor row striping,.x_table-hoverfor hover - Alerts:
.x_alertwith-info-success-errorvariants - Badges:
.x_badgewith status variants; help text:.x_help-block.x_help-inline - Tabs:
.x_nav.x_nav-tabs>li>a, with the active item asli.x_active
<form class="x_form-horizontal" method="post" action="./">
<div class="x_control-group">
<label class="x_control-label">{$lang->title}</label>
<div class="x_controls">
<input type="text" name="title" value="{$config->title}" />
<p class="x_help-block">{$lang->about_title}</p>
</div>
</div>
<div class="x_clearfix" style="text-align:right">
<button type="submit" class="x_btn x_btn-primary">{$lang->cmd_save}</button>
</div>
</form>Legacy unprefixed Bootstrap classes such as .btn and .table are treated the same as x_* inside admin screens, but use x_* in new code.
Module-specific screens and prefixes
For screens that are hard to express with x_* components, such as list cards or dashboards, pick a module prefix and build them yourself. This is the same approach the core modules use.
- Use a single prefix per module. For example, the store admin uses
zmst-and the payment module useszpay- - Even inside your prefix, reference
--zm-*tokens for colors and spacing. That way you get dark mode and future redesigns for free. - Avoid overriding core rules with parent selectors (
.x .something). They break when the priority of the core CSS changes.
Admin template location and registration
Modules with the modern structure keep admin templates in views/admin/*.blade.php (template v2).
modules/mymodule/
├── views/admin/
│ ├── _tabs.blade.php ← 탭 공통 조각
│ ├── config.blade.php
│ └── list.blade.php
└── controllers/Admin.php- If there are several screens, create a tab partial (
_tabs.blade.php) and@includeit in each screen. - Register admin menus with the
menu_nameattribute on action declarations in module.xml. For details, see the Building modules document. - Admin screens have no mid, so build links to other screens as
getUrl('', 'module', 'admin', 'act', ...). If you don't leave the first argument empty, all current request variables are carried along.
What not to do
- Hard-coding colors. Using a white background (
#fff) directly in particular breaks dark mode. Use tokens such asvar(--zm-card). - Loading external CSS frameworks such as Bootstrap on admin screens. They conflict with the core styles.
- Overriding other modules' admin screens with CSS. If an improvement is needed, propose it to the core.
- Overusing inline scripts. The core is expanding CSP on admin screens, so it's safer to move scripts into separate js files.