본문으로 건너뛰기
cccskills
무료GitHub에 공개

handsontable-css-dev

Guides changes to Handsontable themes and CSS, including cell spacing, text, icons, and theme tokens—named styling settings. It explains compatibility rules and the files and checks needed to keep theme changes consistent.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

원문원문 설명 보기

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

설치 방법 보기

이럴 때 유용합니다

  • Adjust cell spacing without disrupting scrolling
  • Add or rename theme tokens
  • Fix text styles affected by the host page
  • Update icons across themes

포함된 파일(1)

  • SKILL.md10.9 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Theme and CSS Development

Themes

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.

Rules

  • CSS custom properties are the public API for theming. Renaming or removing one is a breaking change: keep the old variable working.
  • Renaming or removing a CSS class is a breaking change: keep the old class in the DOM.
  • CSS and JS live in separate files.
  • Every CSS feature must work in the browser-targets.js browsers (eslint-plugin-compat enforces it).

Cell padding comes from the variables

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.

File structure and commands

PathContents
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.

No :has() in stylesheets

The 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.

Text-bearing spans declare their own text metrics

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:

  • A user rule on the guarded class at lower specificity than the guard now loses: .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)).
  • A bare .ht-pagination { font-size } never beat the bar's own .handsontable.ht-pagination rule.
  • Offsets computed from the --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.

Icons

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.

  • Glyph vs. chrome. A component with two pseudo-elements has one drawing the glyph and one drawing a background, border, ring, or hover surface. Only the glyph one is replaced by the <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.
  • RTL mirroring. A mirrored icon carries 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.
  • Migrating a site: move the state rules too. Re-key every :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.
  • Adding or renaming an icon touches four lists that a test or 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.

Adding a new theme token: four layers

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.

Layer 1: static CSS defaults (6 files)

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.

Layer 2: token JS runtime defaults (3 files)

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.

Layer 3: validation allow-list

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).

Layer 4: TypeScript types

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.

Layer 5 (soft): docs

docs/content/guides/styling/theme-customization/theme-customization.md: add the token to the variables reference table (CSS name + JS name, then description).

Naming

JS camelCase maps to CSS kebab-case with the --ht- prefix by convention (no generator): dialogContentPaddingHorizontal is --ht-dialog-content-padding-horizontal.

SCSS consumption

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.

Pre-flight

  1. CSS in all 6 theme files.
  2. JS token in all 3 runtime files.
  3. Key in the validation.ts allow-list.
  4. Key in TokenKey.
  5. npm run test:unit --prefix handsontable --testPathPattern=themes passes.
  6. npm run test:types --prefix handsontable passes.
  7. Docs table updated.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Develop and modify the Handsontable Angular wrapper with guidance on component lifecycle, settings updates, and NgZone, which controls change detection. It also covers standalone examples, global configuration, and the build and test workflow.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Fix settings updates in the Angular wrapper
  • Review change detection around grid operations
  • Update examples to standalone components
handsontable/handsontable2.2만2026년 10월 19일 업데이트

Open local or public web pages in BrowserStack Live for checks on real mobile devices or desktop browsers. The skill prepares temporary access to a local server, launches the requested session, and shares its dashboard link.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Check a local page on an Android device
  • Open a page in Safari on an iPhone or iPad
  • Inspect behavior in a specific desktop browser
handsontable/handsontable2.2만2026년 10월 19일 업데이트

Prepare user-facing changelog entries for source code changes. The skill determines whether an entry is needed, classifies the change, writes a clear title, and uses the project CLI to create a JSON file tied to the pull request.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Add a changelog entry for a bug fix
  • Classify a new feature or behavior change
  • Explain a breaking change and its replacement
handsontable/handsontable2.2만2026년 10월 19일 업데이트

Explore relationships between functions and files using a prebuilt code graph, a map of code dependencies. Trace bugs, inspect callers and execution paths, and assess the impact of refactoring or proposed changes.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Explore callers and dependencies in unfamiliar code
  • Trace a bug across multiple files
  • Assess the impact of renaming or removing code
handsontable/handsontable2.2만2026년 10월 19일 업데이트

Work with Handsontable row and column positions by distinguishing source data, displayed order, and rendered cells. It guides index conversion and helps investigate misplaced data when sorting, filtering, hiding, or moving rows and columns.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Investigating misplaced rows or columns
  • Converting between source and displayed indexes
  • Combining filters with manual column movement
handsontable/handsontable2.2만2026년 10월 19일 업데이트

Create Handsontable documentation examples for JavaScript, TypeScript, React, Angular, and Vue. The skill explains source files, generated JavaScript, required imports and license settings, embedding examples in guides, and checking that each framework version displays correctly.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Adding a code example to a feature guide
  • Preparing examples for multiple frameworks
  • Generating JavaScript examples from TypeScript
handsontable/handsontable2.2만2026년 10월 19일 업데이트

handsontable의 스킬 모두 보기

이 스킬의 문제 신고