Skip to main content

ui-styling

Guide for the visual style, structure, and shared building blocks used by Semantic Link Labs interactive UI tools (HTML widgets and anywidget-based widgets). Use this when adding a new interactive UI, modifying an existing one, or adding shared visual components.

소스 정보

저장소
microsoft/semantic-link-labs
최근 소스 활동
2026년 8월 11일 05:19
감지된 SKILL.md 언어
영어
스타
586
포크
197

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
ui-styling
description
Guide for the visual style, structure, and shared building blocks used by Semantic Link Labs interactive UI tools (HTML widgets and anywidget-based widgets). Use this when adding a new interactive UI, modifying an existing one, or adding shared visual components.
# UI Styling for Interactive Tools This skill describes the styling conventions, shared building blocks, and architectural patterns used by all interactive UI tools in Semantic Link Labs so that every tool has a consistent, elegant design. ## When to Use This Skill Use this skill when you need to: - Add a **new interactive UI** function to the library. - Modify an existing interactive UI (e.g. `vertipaq_analyzer`, `delta_analyzer`, `perspective_editor`). - Add new **shared visual components** (icons, theme variables, headers, etc.) that should be reused across tools. - Decide between a **static-HTML** widget and an **anywidget**-based widget. --- ## The Two UI Patterns Semantic Link Labs has exactly two supported patterns for interactive UI tools. Always pick one based on whether the UI needs to call back into Python after the initial render. | Pattern | When to use | Reference implementations | |---------|-------------|---------------------------| | **Static-HTML widget** (the *Vertipaq style*) | The UI is fully driven by the data computed in Python *before* render. All interactivity (filtering, sorting, tab switching, theme toggle, column resizing, etc.) is done in **pure browser-side JavaScript**. No Python code runs after `display(HTML(...))`. | `sempy_labs.semantic_model._vertipaq_analyzer.vertipaq_analyzer`, `sempy_labs._delta_analyzer.delta_analyzer` | | **anywidget widget** (the *Perspective Editor style*) | The UI must run Python code in response to user actions (e.g. write back to a semantic model, refresh data from a REST API, perform long-running operations). State is synced between the JS frontend and the Python backend via `traitlets`. | `sempy_labs.semantic_model._perspective_editor.perspective_editor` | > **Rule of thumb:** if the only thing the user does is view, sort, filter, or switch between pre-computed data, use the **static-HTML** pattern. If they can *change* something that must persist (model edits, refresh triggers, server calls), use **anywidget**. --- ## Shared Building Blocks: `sempy_labs._ui_components` `src/sempy_labs/_ui_components.py` is the single source of truth for everything visual that should be consistent across tools. **Both patterns must source their visual primitives from this module.** If you need a new shared visual component (a new icon, a new helper, a new themed control), add it here so every tool can pick it up. ### What lives there today | Export | Purpose | |--------|---------| | `ICONS` | Dict of monochrome SVG icons. All use `stroke="currentColor"` / `fill="currentColor"` so they adapt to light and dark themes automatically. Keys include tabular-object icons (`table`, `calculation_group`, `column`, `column_chunk`, `measure`, `hierarchy`, `calculation_item`, `partition`, `relationship`), tree/navigation icons (`caret_right`, `folder`, `level`), and UI/action icons (`sun`, `moon`, `search`, `plus`, `play`, `stop`, `refresh`, `swap`, `sort_asc`, `sort_desc`, `panel_collapse`, `panel_expand`, `builder`, `close`, `fullscreen`, `fullscreen_exit`). | | `LIGHT_THEME_VARS`, `DARK_THEME_VARS` | CSS custom-property blocks defining the Apple-inspired light and dark palettes. Always reference colors via these `--ui-*` tokens, never hard-coded hex values. Includes semantic tokens for hover backgrounds (`--ui-bg-hover`), on-accent text (`--ui-on-accent`), and destructive/error states (`--ui-danger*`). | | `SYNTAX_HIGHLIGHT_VARS` | Theme-independent `--ui-syntax-*` token block for colorizing DAX/code in an editor. Inject once into the widget's base scope (it is the same in light and dark). | | `HEADER_CSS`, `scoped_header_css(root_selector)` | Standard widget header styles (title + dataset/workspace subtitle) **and the four standard header controls** (`.sl-theme-btn`, `.sl-change-btn`, `.sl-reload-btn`). `scoped_header_css` prefixes every rule with the root selector so the styles win against notebook host CSS (e.g. Jupyter's `.jp-RenderedHTMLCommon button`). Every tool must inject this, even when it builds its own header markup. | | `render_header_html(title, dataset_name, workspace_name, theme_btn_id, dark_mode, fullscreen_btn_id)` | Renders the standard header markup. Pass `fullscreen_btn_id` to include a full-screen toggle button next to the theme toggle. | | `theme_toggle_script(btn_id, root_selector, dark_class)` | Returns a `<script>` block that wires the theme toggle button to flip a `dark_class` on the root element and swap the sun/moon icon. | | `fullscreen_css(root_selector, fullscreen_class, container_selector=None, bg_var)` | Returns the CSS for a widget's full-screen state (covers both the native `:fullscreen` pseudo-class and the `fullscreen_class` CSS-overlay fallback). Pass `container_selector` when an inner element carries the card styling; pass `bg_var` to match the widget's background token. | | `fullscreen_toggle_script(btn_id, root_selector, fullscreen_class)` | Returns a `<script>` block that wires a full-screen toggle button for **static-HTML** widgets. Pair with `render_header_html(..., fullscreen_btn_id=...)` and `fullscreen_css`. | | `fullscreen_setup_js(func_name="sllsSetupFullscreen")` | Returns a JS function definition that wires a full-screen toggle button for **anywidget** widgets. Embed once at the top of the ESM module, then call `func_name(root, btn, fullscreenClass, enterSvg, exitSvg)` after creating the button. | | `display_html_widget(html, fallback=True)` | Renders a self-contained HTML string (styles + markup + inline `<script>`) via a lightweight anywidget so it lives in the notebook **webview's light DOM** instead of the sandboxed `srcdoc` iframe used for raw `display(HTML)`. Use this for static-HTML widgets so the full-screen toggle gets the native Fullscreen API (matching the anywidget tools). Falls back to `display(HTML(html))` when `anywidget` is unavailable. | | `ATTRIBUTION_CSS`, `scoped_attribution_css(root_selector)`, `render_attribution_html(extra_links=None)` | "Powered by Semantic Link Labs" attribution shown at the bottom of every widget, with an optional list of extra `(label, url)` links. | | `SEARCH_SELECT_CSS`, `SEARCH_SELECT_JS` | The standard **searchable single-select** picker (`createSearchSelect({ placeholder, searchPlaceholder, ariaLabel, emptyLabel, onChange })`). Every workspace / semantic model / item picker must use this control — never a plain `<select>` — so long lists can always be filtered by typing. Inject the CSS into the widget stylesheet and the JS into the widget's ESM module, then drive the returned controller with `setOptions(items, value)`, `setEmptyLabel(text)` and `setDisabled(flag)`. Reference implementations: `semantic_model._find_unused_objects`, `semantic_model._bpa`. | ### When to extend `_ui_components` Add a new export to `_ui_components.py` whenever the same visual element appears (or *should* appear) in more than one tool. Typical candidates: a new icon, a shared button style, a status-pill style, a confirmation-dialog component, a toast/notification helper. Do **not** copy-paste CSS or SVGs between widgets — promote them to `_ui_components` instead. --- ## Standard Header Controls (mandatory) Four controls recur in nearly every tool. They must look and behave identically everywhere, so both their icon and their chrome come from `_ui_components` — a tool never defines its own size, radius, border or icon for them. The `sempy_labs.semantic_model.test` (DAX Perf Optimizer) widget is the reference implementation. | Control | Class | Icon | Footprint | |---------|-------|------|-----------| | Light/dark mode | `sl-theme-btn` | `ICONS["sun"]` / `ICONS["moon"]` | 32×32 circle, 18px icon | | Full screen | `sl-theme-btn` | `ICONS["fullscreen"]` / `ICONS["fullscreen_exit"]` | 32×32 circle, 18px icon | | Change model / workspace | `sl-change-btn` | `ICONS["swap"]` | 32×32, 8px radius, 18px icon | | Reload (workspaces, models, lists) | `sl-reload-btn` | `ICONS["refresh"]` | 32×32 circle, 14px icon | Rules: - Inject `scoped_header_css(root_selector)` into the widget stylesheet, and use the class names above. Do **not** re-declare `.sl-theme-btn`, `.sl-change-btn` or `.sl-reload-btn` in a tool — `tests/test_ui_header_controls.py` fails if a tool restyles them. - While a reload is in flight, add `sl-spinning` to the reload button; the shared CSS animates the icon. - If the tool uses `--slls-*` (or other) palette names, alias the `--ui-*` tokens the shared CSS reads: `--ui-surface`, `--ui-surface-2`, `--ui-border-strong`, `--ui-text`, `--ui-text-secondary`, `--ui-text-tertiary`, `--ui-accent`, `--ui-accent-soft`. - Tools rendering their header with `render_header_html` get these for free via `theme_btn_id`, `fullscreen_btn_id` and `picker_btn_id`. For an extra button that changes the model/workspace, pass `"base": "sl-change-btn"` in `extra_buttons`. - Buttons that are *not* one of these four (e.g. expand/collapse, delete, tool-specific actions) keep their own tool-scoped classes. ### Workspace / semantic model pickers Always resolve the workspace with `resolve_workspace_name_and_id(workspace)` **even when the caller passed nothing**, and seed the picker with the result, so the workspace dropdown opens pre-selected on the current workspace. Passing `None` resolves to the attached lakehouse's workspace, or the notebook's. --- ## Design Tokens (the visual language) All interactive UIs share one visual language. Stick to these tokens — do not introduce one-off colors, fonts, radii, or shadows. ### Typography - Font stack: `-apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Helvetica Neue", Helvetica, Arial, sans-serif`. - Enable font smoothing: `-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;`. - Title (22px / 600 / `-0.01em` letter-spacing) and subtitle (≈12.5px / `--ui-text-secondary`) are produced by `render_header_html` — do not re-implement them. - Use `font-variant-numeric: tabular-nums` for any numeric column or large numeric value. ### Color tokens (from `LIGHT_THEME_VARS` / `DARK_THEME_VARS`) | Token | Use for | |-------|---------| | `--ui-bg`, `--ui-bg-secondary`, `--ui-bg-tertiary`, `--ui-bg-solid` | Surfaces, in increasing emphasis levels | | `--ui-bg-hover` | Solid hover background for controls/buttons whose base is `--ui-bg` | | `--ui-surface`, `--ui-surface-2` | Translucent overlays / hover backgrounds | | `--ui-border`, `--ui-border-strong` | Subtle and strong borders | | `--ui-text`, `--ui-text-secondary`, `--ui-text-tertiary` | Primary, secondary, tertiary text | | `--ui-accent`, `--ui-accent-hover`, `--ui-accent-soft` | Brand accent (links, focus rings, active tab indicator, primary buttons, data bars) | | `--ui-on-accent` | Text/icon color placed on top of an `--ui-accent` (or `--ui-danger`) fill — i.e. "white" | | `--ui-danger`, `--ui-danger-hover` | Destructive button fill + hover (e.g. a Stop button) | | `--ui-danger-bg`, `--ui-danger-border`, `--ui-danger-text` | Inline error/alert box background, border, and text (themed for light + dark) | | `--ui-shadow-sm`, `--ui-shadow-md`, `--ui-shadow-lg` | Elevation | Never hard-code `#fff`, red error colors, or any other literal — always go through a token. If a destructive action, error banner, or white-on-accent label is needed, use the danger / on-accent tokens above rather than inlining hex values. ### Code / DAX syntax-highlight palette (`SYNTAX_HIGHLIGHT_VARS`) For colorizing DAX (or similar code) inside an editor/highlighter, inject the theme-independent `SYNTAX_HIGHLIGHT_VARS` block once into the widget's **base** scope (it intentionally renders the same in light and dark, so do not override it in the dark block). Reference these `--ui-syntax-*` tokens — never the raw hex values. | Token | Token class it colors | |-------|-----------------------| | `--ui-syntax-keyword`, `--ui-syntax-function` | Keywords and function names | | `--ui-syntax-variable` | Variables | | `--ui-syntax-number` | Numeric literals | | `--ui-syntax-virtual-column` | Virtual / measure-ref columns | | `--ui-syntax-string` | String literals | | `--ui-syntax-operator`, `--ui-syntax-punctuation` | Operators and punctuation | See `sempy_labs.semantic_model._test_dax.test` for the reference usage (it injects `SYNTAX_HIGHLIGHT_VARS` into its `.dtx` base block alongside `LIGHT_THEME_VARS`). If a tool needs its own derived names (e.g. `--vpx-text`), alias them to the `--ui-*` tokens inside the tool's scoped block — see `_vertipaq_analyzer.py` for the pattern. This keeps the palette centralized while letting tools use short, local names. ### Shape and motion - Radii: `12px` (containers), `8px` (controls/cards) — exposed as `--vpx-radius` / `--vpx-radius-sm` style aliases when needed. - Transitions: `0.25s cubic-bezier(0.4, 0, 0.2, 1)` for color/border/background; `120ms ease` for small button state changes; `80ms` for press scale.
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기