Use when developing or modifying the @handsontable/angular-wrapper package - Angular components with decorators, NgZone performance optimization, and ng-packagr build system
日本語の概要は準備中です。原文の説明を表示しています。
Use when working with Handsontable themes, CSS custom properties, SCSS files, theme tokens, or visual styling - covers theme architecture, CSS variable API, the strict CSS/JS separation rule, and the four-layer process for adding or renaming theme tokens
インストールする前に、エージェントに与えられる指示の中身を確認できます。
Three themes, each with a -no-icons variant (6 bundles): ht-theme-main (default), ht-theme-classic, ht-theme-horizon, and ht-theme-main-no-icons, ht-theme-classic-no-icons, ht-theme-horizon-no-icons. Test all 3 themes after any styling change.
browser-targets.js browsers (eslint-plugin-compat enforces it).StylesHandler#getDefaultRowHeight() computes --ht-line-height + 2 * --ht-cell-vertical-padding + border-bottom-width, and Walkontable sizes a table's scroll range from the summed row heights. A padding written onto a td leaves the variable at the theme value, so the scroll range comes out wrong. Override --ht-cell-vertical-padding / --ht-cell-horizontal-padding and derive the td padding from them.
A nested grid built inside a hidden container masks this: its styles cache is empty, the derived row height reads null, and the engine measures the DOM.
| Path | Contents |
|---|---|
src/styles/ | Base SCSS |
src/themes/ | Theme SCSS |
handsontable/styles/ | Compiled CSS, gitignored (handsontable/.gitignore), never committed; regenerated by build:styles/build:themes-css |
build:styles compiles SCSS, build:themes-* builds theme assets, npm run stylelint --prefix handsontable lints CSS/SCSS.
:has() in stylesheetsThe stylelint rule handsontable/no-has-selector (error) bans :has() in src/**/*.{css,scss}: in Chrome it re-runs style invalidation across the host page on every grid DOM mutation, and the grid mutates on every scroll render. Drive the style from a class that JS toggles on the target element (reference pattern: SelectionManager #markActiveHeaderNeighbors and the -seam taggers).
The rule lives in .config/plugin/stylelint/, a pnpm file: dependency that is copied, not symlinked: run pnpm install after editing it.
An exception for state not re-evaluated during a scroll (dialog focus, dropdown selection, offscreen .htGhostTable) uses // stylelint-disable-next-line handsontable/no-has-selector -- <reason> with the reason why it is off the scroll path.
Every <span> (or other inline element) the grid renders with visible text declares font-size, line-height, font-weight, letter-spacing, and font-family itself. A host rule on the bare element (span { font-size: 20px }, specificity (0,0,1)) beats an inherited value (#11306, DEV-75). The older guards cover two properties only: span.colHeader/span.rowHeader (tokens in _base.scss), .htCheckboxRendererLabel, .ht-sheets-bar__tab-label; their font-weight, letter-spacing, and font-family still leak.
Pattern: the component root (.ht-pagination, td, .ht-sheets-bar) carries the token (font-size: var(--ht-font-size)), and every text-bearing span below it declares the five properties as inherit. That wins over the host's element selector and follows a user's override on the root, provided the override beats the root's rule on specificity. Source order does not decide it, because the core stylesheet is injected into <head> at init and often loads after the user's CSS. Core's cell rule .handsontable :where(...) > td is (0,1,1): a user's .handsontable td ties and loses on order, .handsontable tbody td (0,1,2) wins. The bar's .handsontable.ht-pagination is (0,2,0): div.handsontable.ht-pagination (0,2,1) wins. State three limits in the PR:
.ht-multi-select-chip { font-size: 16px } (guard (0,2,0)), .ht-page-navigation-section__label { font-weight: 600 }, and .handsontable .ht-page-navigation-section__label (guard .handsontable.ht-pagination ... (0,3,0))..ht-pagination { font-size } never beat the bar's own .handsontable.ht-pagination rule.--ht-line-height token (multiselect and autocomplete arrow top, chip end padding) do not follow a user's td line-height override.Scope the declarations to the guarded classes. Do not add a blanket .handsontable span { ... } rule: its specificity (0,1,1) overrides a user's .my-class on spans inside custom renderers.
tests/e2e/host-span-styles.spec.ts (fixture tests/fixtures/demo/host-span-styles.html) hosts a hostile span {} rule and asserts the guarded spans against their cascade parent. Its user-override tests prepend their rule to <head> (HostSpanStylesPage.prependUserStyles). Add every new text-bearing span to that spec. The fixture loads the compiled handsontable/styles/*.min.css and a dist/ bundle that injects the core stylesheet again at init, so after an SCSS edit run the full npm --prefix handsontable run build before trusting a spec run; build:styles and build:styles.min alone leave the bundle's copy stale and a reverted guard still passes.
Every built-in icon is a real element: <i class="ht-icon ht-icon-<kebab-name>" aria-hidden="true"></i>, built by two functions in handsontable/src/themes/engine/icons.ts:
createIcon(hot, name, options) builds a fresh icon (options: { flipInRtl?, className? }).syncIcon(hot, container, slotClass, name, options) keeps exactly one icon in a slot a hook re-renders: creates it when missing, keeps it when the name is unchanged, replaces it when the name changed, and removes it when name is null. Use it wherever a header/draw hook decides per render whether an icon shows; do not hand-roll createIcon plus manual remove/replace bookkeeping there.The glyph comes from the per-theme --ht-icon-<name> variable plus a generated .ht-icon-<name> rule (mask-image: var(--ht-icon-<name>); background-color: currentColor). .ht-icon (handsontable/src/styles/components/core/_icon.scss) carries no paint, no background-color and no mask-image: a -no-icons bundle ships neither the variable nor the .ht-icon-<name> rule, so mask-image resolves to none and the icon renders as nothing. Paint on the base class would render every icon in a -no-icons bundle as a solid square. Add paint only to a generated .ht-icon-<name> rule.
<i> element (its @include mixins.pseudo / content: '' dropped); the chrome one keeps its content unchanged. Read both &::before/&::after rules before touching either (::before is not always the glyph). Mixing them up shipped four times: radio focus ring, sheets-bar chevron hover, checkbox box, collapsible-column indicator background.ht-icon--flip-rtl ([dir="rtl"] .ht-icon--flip-rtl { transform: scaleX(-1); }). Its positioning rule must never also set transform (such as translateY(-50%)): the declarations collide and the mirror silently stops, visible only under [dir="rtl"]. Center it with a negative margin or a flex/grid container.:hover, :focus, :disabled, :checked, or .is-active rule that colored the old pseudo-element onto .ht-icon (or a class the <i> carries); otherwise the glyph stays visually dead on state changes.tsc cross-checks: the generator side in handsontable/scripts/themes/figma/.ai/CONVENTIONS.md ("Adding or changing an icon") and IconKey/ICON_NAMES/VALID_ICON_KEYS at runtime.For a --ht-<component>-<property> variable and its JS token. A rename or removal follows the same playbook plus a legacy-alias path. Group the new entry next to related ones in each file.
handsontable/src/themes/static/css/theme/ht-theme-{main,classic,horizon}{,-no-icons}.css
Define --ht-<kebab-case-name> and its resolved default per theme, usually via var(...) of lower-level tokens so overrides keep cascading. All themes declare the same key set. Symptom when missing: the variable is undefined in DevTools while ThemeBuilder tests that skip rendered styles pass.
handsontable/src/themes/static/variables/tokens/{main,classic,horizon}.ts
These drive ThemeBuilder. camelCase keys mirror the CSS variable (paginationButtonBorderColor for --ht-pagination-button-border-color); values use 'tokens.otherTokenName' strings or primitive arrays like ['colors.palette.100', 'colors.palette.700']. Symptom when missing: createTheme() ignores the token.
handsontable/src/themes/engine/utils/validation.ts: add the key to the VALID_TOKEN_KEYS Set, in the matching semantic section (e.g. // Pagination). Symptom when missing: ThemeBuilder logs [ThemeBuilder] Unknown token key: "xxx", and src/themes/engine/__tests__/builder.unit.js fails "should not warn for unknown token keys when using built-in tokens in createTheme" (it iterates every mainTokens key to catch layer 2/3 drift).
handsontable/src/themes/types.ts: add the key to the TokenKey union. Edit the source; tmp/themes/types.d.ts is generated by build:types. Symptom when missing: npm run test:types --prefix handsontable (tsc -p ./test/types) fails with a TokenKey assignability error.
docs/content/guides/styling/theme-customization/theme-customization.md: add the token to the variables reference table (CSS name + JS name, then description).
JS camelCase maps to CSS kebab-case with the --ht- prefix by convention (no generator): dialogContentPaddingHorizontal is --ht-dialog-content-padding-horizontal.
Reference the variable from the component SCSS under src/styles/components/ (e.g. _pagination.scss) as var(--ht-<name>). Use the var(--ht-<name>, <fallback>) form only for a legacy compatibility fallback, because a fallback masks missing-variable bugs.
validation.ts allow-list.TokenKey.npm run test:unit --prefix handsontable --testPathPattern=themes passes.npm run test:types --prefix handsontable passes.まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Use when developing or modifying the @handsontable/angular-wrapper package - Angular components with decorators, NgZone performance optimization, and ng-packagr build system
日本語の概要は準備中です。原文の説明を表示しています。
Use when testing a local or public URL on a real device via BrowserStack, opening a live browser session on Android, iOS, or desktop browsers, or when the user mentions BrowserStack, real device testing, mobile testing, cross-browser testing, touch testing, or wants to verify behavior on a specific phone, tablet, or browser version. Also trigger when the user says "test this on Android/iPhone/Safari/Chrome mobile", asks to open a live session, or mentions demo-mobile.html, even if they don't mention BrowserStack by name.
日本語の概要は準備中です。原文の説明を表示しています。
Use when a source code change needs a changelog entry, or before committing and pushing any bug fix, feature, or behavior change to source code - detecting when entries are required, categorizing changes correctly (added/changed/fixed/deprecated/removed/security), writing user-facing titles, and creating the JSON entry in .changelogs/
日本語の概要は準備中です。原文の説明を表示しています。
Query the pre-built code-review-graph knowledge graph (Tree-sitter, whole monorepo) instead of walking call chains with Grep+Read – 2-6x cheaper, and it catches dynamic dispatch that grep misses. Use for ANY task that spans multiple files, even when the user never mentions a graph or asks for it - fixing or tracing a bug ("fix this bug", "why does X happen", "trace this"), exploring unfamiliar code ("how does X work", "who calls X", "what imports Y", "where is X used or handled"), planning or doing a refactor ("rename X", "is it safe to change or remove X", "what would break", "blast radius", "find dead code"), or reviewing changes ("review this PR", "review this branch or diff"). When you are about to Grep for a symbol's callers, callees, or importers, use this skill.
日本語の概要は準備中です。原文の説明を表示しています。
Use when working with row or column indexes in Handsontable - translating between physical, visual, and renderable coordinates, using IndexMapper, or debugging index-related bugs where rows or columns appear in wrong positions
日本語の概要は準備中です。原文の説明を表示しています。
Use when creating code examples for documentation pages - JavaScript, TypeScript, React, Angular, and Vue variants with proper imports, registration, and license key
日本語の概要は準備中です。原文の説明を表示しています。