Conversation
…n tokens - Updated landing.css to enhance Starlight structure overrides and remove unnecessary styles. - Introduced layout.css for header, sidebar, navigation, footer, and mobile menu styles. - Added tokens.css for design tokens including colors, fonts, shadows, and radii. - Created typography.css for consistent body and heading styles across the site. - Updated uno.config.ts to define font families for display and code.
…ved layout and design
- Rearranged catalog entries in pnpm-workspace.yaml for better organization. - Added new dependencies: eslint-plugin-astro. - Updated dark.svg and light.svg logos with new design elements, including additional shapes and color adjustments.
…tation - Changed the "Get Started" link in Hero component from "/docs/" to "/overview/introduction". - Added a new introduction page for the documentation with features and benefits of the UI component library. - Enhanced styles for various components including cards, tabs, and markdown content for better readability and aesthetics. - Adjusted typography settings for headings and paragraphs to improve visual hierarchy and consistency. - Updated CSS tokens for colors and layout dimensions to refine the overall design. - Implemented smooth transitions for theme changes and improved accessibility features across components.
…ed UI consistency
…cumentation - Introduced DataAttrTable component to render component data attributes. - Added EmitsTable component to display emitted events with descriptions. - Created PropsTable component to showcase component props with types and default values. fix: update MobileTableOfContents and Pagination styles - Removed unnecessary uppercase text transformation in MobileTableOfContents. - Adjusted font styles in Pagination for consistency. chore: enhance Avatar component examples and styles - Added event handling and root provider examples for Avatar component in Vue. - Created new CSS files for Avatar event and root provider styles. style: remove uppercase transformations from various components - Cleaned up styles across multiple components to remove uppercase text transformations for better readability. chore: update package dependencies and workspace configuration - Added @destyler/docs package to pnpm workspace and updated lockfile.
There was a problem hiding this comment.
Pull request overview
This PR adds and styles a new Astro/Starlight documentation site for the UI library, along with supporting tooling for live component previews and generated type API references.
Changes:
- Added a
docs/workspace package (Astro + Starlight + UnoCSS) with custom header/sidebar/TOC/pagination and landing page sections. - Implemented live Vue/React example preview + highlighted source display, plus a script to generate API reference JSON from framework
dist/*.d.ts. - Updated example components and logo assets, plus workspace scripts/configs to support docs development and linting.
Reviewed changes
Copilot reviewed 77 out of 82 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| public/logos/light.svg | Updates the light logo asset used by the docs header/branding. |
| public/logos/dark.svg | Updates the dark logo asset used by the docs header/branding. |
| pnpm-workspace.yaml | Adds docs workspace + catalogs for docs/styles/icons dependencies. |
| packages/vue/src/components/breadcrumbs/examples/RootProvider.vue | Adjusts Vue breadcrumbs example to use reactive props + separator logic. |
| packages/vue/src/components/breadcrumbs/examples/Context.vue | Tweaks Vue breadcrumbs context example markup for docs display. |
| packages/vue/src/components/avatar/examples/RootProvider.vue | Updates Vue avatar provider example to change image source dynamically. |
| packages/vue/src/components/avatar/examples/Event.vue | Updates Vue avatar event example to display status in the UI. |
| packages/vue/src/components/avatar/examples/Basic.vue | Minor formatting cleanup in Vue avatar basic example. |
| packages/vue/src/components/aspect-ratio/examples/Square.vue | Moves inline styles to class-based styling for docs theming. |
| packages/vue/src/components/aspect-ratio/examples/RootProvider.vue | Simplifies the image example and removes inline sizing styles. |
| packages/vue/src/components/aspect-ratio/examples/Portrait.vue | Simplifies the image example and removes inline sizing styles. |
| packages/vue/src/components/aspect-ratio/examples/Basic.vue | Simplifies the image example and removes inline sizing styles. |
| package.json | Adds docs scripts + includes eslint-plugin-astro dependency. |
| eslint.config.mjs | Adds Astro ESLint config for files under docs/**. |
| docs/uno.config.ts | Adds UnoCSS config for docs styling + icons. |
| docs/tsconfig.json | Adds strict Astro TS config for docs package. |
| docs/src/utils/shiki.ts | Adds Shiki core highlighter utility with dual themes. |
| docs/src/styles/typography.css | Introduces docs typography overrides. |
| docs/src/styles/tokens.css | Introduces docs design token variables and base theming. |
| docs/src/styles/layout.css | Introduces docs layout overrides (header/sidebar/footer/search). |
| docs/src/styles/landing.css | Adds landing-page-specific structural overrides. |
| docs/src/styles/content.css | Adds markdown content styling overrides. |
| docs/src/styles/components/token.css | Adds demo-specific token variables used by component previews. |
| docs/src/styles/components/components.css | Aggregates component demo styles into a single import. |
| docs/src/styles/components/breadcrumbs/rootprovider.css | Adds docs styles for breadcrumbs RootProvider preview. |
| docs/src/styles/components/breadcrumbs/context.css | Adds docs styles for breadcrumbs Context preview. |
| docs/src/styles/components/breadcrumbs/breadcrumbs.css | Aggregates breadcrumbs demo css imports. |
| docs/src/styles/components/breadcrumbs/basic.css | Adds docs styles for breadcrumbs Basic preview. |
| docs/src/styles/components/avatar/rootprovider.css | Adds docs styles for avatar RootProvider preview. |
| docs/src/styles/components/avatar/event.css | Adds docs styles for avatar Event preview. |
| docs/src/styles/components/avatar/basic.css | Adds docs styles for avatar Basic preview. |
| docs/src/styles/components/avatar/avatar.css | Aggregates avatar demo css imports. |
| docs/src/styles/components/aspect-ratio/square.css | Adds docs styles for aspect-ratio Square preview. |
| docs/src/styles/components/aspect-ratio/root-provider.css | Adds docs styles for aspect-ratio RootProvider preview. |
| docs/src/styles/components/aspect-ratio/portrait.css | Adds docs styles for aspect-ratio Portrait preview. |
| docs/src/styles/components/aspect-ratio/basic.css | Adds docs styles for aspect-ratio Basic preview. |
| docs/src/styles/components/aspect-ratio/aspect-ratio.css | Aggregates aspect-ratio demo css imports. |
| docs/src/styles/components.css | Adds global UI component styling overrides (tabs, cards, code, etc.). |
| docs/src/styles/bootstrap.css | Centralizes docs CSS imports and load order. |
| docs/src/env.d.ts | Adds Astro + Vue module typing for docs integrations. |
| docs/src/content/docs/overview/introduction.mdx | Adds Introduction docs content. |
| docs/src/content/docs/index.mdx | Adds custom landing page (splash template). |
| docs/src/content/docs/components/breadcrumbs.mdx | Adds Breadcrumbs component documentation page. |
| docs/src/content/docs/components/avatar.mdx | Adds Avatar component documentation page. |
| docs/src/content/docs/components/aspect-ratio.mdx | Adds Aspect Ratio component documentation page. |
| docs/src/content.config.ts | Adds Starlight content collection configuration. |
| docs/src/components/landing/Steps.astro | Adds landing “steps” section with Shiki-highlighted code blocks. |
| docs/src/components/landing/Hero.astro | Adds landing hero section. |
| docs/src/components/landing/Features.astro | Adds landing features section. |
| docs/src/components/landing/Components.astro | Adds landing “components list” section. |
| docs/src/components/landing/CTA.astro | Adds landing CTA + custom landing footer. |
| docs/src/components/header/HeaderThemeToggle.astro | Adds a custom theme toggle control for docs header. |
| docs/src/components/header/HeaderSearch.astro | Restyles Starlight search trigger for docs header. |
| docs/src/components/header/HeaderLogo.astro | Adds theme-aware header logo rendering. |
| docs/src/components/header/HeaderGitHub.astro | Adds GitHub icon link in header. |
| docs/src/components/header/HeaderFramework.astro | Adds framework switcher for Vue/React content switching. |
| docs/src/components/VueExample.vue | Loads Vue SFC examples dynamically for previews. |
| docs/src/components/TableOfContents.astro | Adds custom desktop TOC with active-heading tracking. |
| docs/src/components/Sidebar.astro | Adds custom sidebar with state persistence. |
| docs/src/components/ReactExample.tsx | Loads React examples lazily for previews. |
| docs/src/components/PropsTable.astro | Renders props table for generated API reference. |
| docs/src/components/Pagination.astro | Adds custom docs pagination UI. |
| docs/src/components/MobileTableOfContents.astro | Adds custom mobile TOC with active-heading tracking. |
| docs/src/components/MobileMenuToggle.astro | Adds custom mobile menu toggle control. |
| docs/src/components/Header.astro | Composes the custom header from subcomponents. |
| docs/src/components/FrameworkContent.astro | Adds framework-scoped content wrapper for MDX pages. |
| docs/src/components/EmitsTable.astro | Renders emits/events table for generated API reference. |
| docs/src/components/DataAttrTable.astro | Renders data-attribute table for generated API reference. |
| docs/src/components/ComponentTypes.astro | Loads and renders generated per-component type JSON. |
| docs/src/components/ComponentPreview.astro | Adds live preview + “show code/copy code” for component examples. |
| docs/scripts/generate-type-docs.ts | Adds CLI tool to generate docs type JSON from framework d.ts/machine output. |
| docs/public/logos | Links docs public logos to shared public assets. |
| docs/package.json | Adds docs package definition + scripts + deps for Astro/Starlight site. |
| docs/astro.config.ts | Adds Astro config integrating Starlight, UnoCSS, Vue/React, and custom components. |
| .gitignore | Ignores generated docs type JSON output directory. |
Comments suppressed due to low confidence (3)
docs/src/components/landing/Hero.astro:1
- This copy says the library provides “fully styled” components, but elsewhere (and in this PR) the docs emphasize components ship with zero styles/unstyled primitives. Updating the hero tagline to match the unstyled positioning will prevent confusion and contradictory messaging.
docs/src/components/landing/CTA.astro:1 - Correct the casing/spelling of 'ToDay' to 'Today' for a consistent, professional CTA headline.
docs/src/content/docs/components/aspect-ratio.mdx:1 - The React anatomy section is currently an empty code block, which reads like a rendering bug in the docs. Either add the React example snippet or remove the React block until it’s ready.
| const themeToggle = document.getElementById('ds-theme-toggle')!; | ||
| themeToggle.addEventListener('click', () => { | ||
| const current = document.documentElement.dataset.theme; | ||
| const next = current === 'dark' ? 'light' : 'dark'; | ||
| document.documentElement.dataset.theme = next; | ||
| localStorage.setItem('starlight-theme', next); | ||
| }); |
There was a problem hiding this comment.
This inline browser script contains TypeScript-only syntax (! non-null assertion). In production it will throw a syntax error and break the theme toggle. Fix by either converting the script to valid JavaScript (remove ! and add a runtime null-check) or by marking the script as TypeScript (e.g., lang=\"ts\") so Astro/Vite transpiles it.
| const themeToggle = document.getElementById('ds-theme-toggle')!; | |
| themeToggle.addEventListener('click', () => { | |
| const current = document.documentElement.dataset.theme; | |
| const next = current === 'dark' ? 'light' : 'dark'; | |
| document.documentElement.dataset.theme = next; | |
| localStorage.setItem('starlight-theme', next); | |
| }); | |
| const themeToggle = document.getElementById('ds-theme-toggle'); | |
| if (themeToggle) { | |
| themeToggle.addEventListener('click', () => { | |
| const current = document.documentElement.dataset.theme; | |
| const next = current === 'dark' ? 'light' : 'dark'; | |
| document.documentElement.dataset.theme = next; | |
| localStorage.setItem('starlight-theme', next); | |
| }); | |
| } |
| const trigger = document.getElementById('ds-fw-trigger')!; | ||
| const dropdown = document.getElementById('ds-fw-dropdown')!; | ||
| const options = document.querySelectorAll<HTMLButtonElement>('.ds-fw-option'); | ||
|
|
||
| function setFramework(fw: string) { | ||
| document.documentElement.dataset.framework = fw; | ||
| localStorage.setItem(STORAGE_KEY, fw); | ||
| options.forEach((opt) => { | ||
| const isActive = opt.dataset.frameworkOption === fw; | ||
| opt.setAttribute('aria-selected', String(isActive)); | ||
| }); | ||
| } | ||
|
|
||
| function openDropdown() { | ||
| dropdown.classList.remove('hidden'); | ||
| trigger.setAttribute('aria-expanded', 'true'); | ||
| } | ||
|
|
||
| function closeDropdown() { | ||
| dropdown.classList.add('hidden'); | ||
| trigger.setAttribute('aria-expanded', 'false'); | ||
| } | ||
|
|
||
| // Initialize | ||
| const saved = localStorage.getItem(STORAGE_KEY) || 'vue'; | ||
| setFramework(saved); | ||
|
|
||
| trigger.addEventListener('click', (e) => { | ||
| e.stopPropagation(); | ||
| const isOpen = !dropdown.classList.contains('hidden'); | ||
| isOpen ? closeDropdown() : openDropdown(); | ||
| }); | ||
|
|
||
| options.forEach((opt) => { | ||
| opt.addEventListener('click', () => { | ||
| const fw = opt.dataset.frameworkOption; | ||
| if (fw) setFramework(fw); | ||
| closeDropdown(); | ||
| }); | ||
| }); | ||
|
|
||
| // Close on outside click | ||
| document.addEventListener('click', (e) => { | ||
| if (!dropdown.classList.contains('hidden') && !(e.target as HTMLElement).closest('.ds-framework-switcher')) { | ||
| closeDropdown(); | ||
| } | ||
| }); | ||
|
|
||
| // Close on Escape | ||
| document.addEventListener('keydown', (e) => { | ||
| if (e.key === 'Escape' && !dropdown.classList.contains('hidden')) { | ||
| closeDropdown(); | ||
| trigger.focus(); | ||
| } | ||
| }); |
There was a problem hiding this comment.
This client-side <script> uses TypeScript syntax (!, generic type params on querySelectorAll, and type annotations like fw: string). Unless this is explicitly transpiled (e.g., <script lang=\"ts\">), the browser will fail to parse it. Convert to plain JS + runtime checks, or switch the script to TS so it gets compiled.
| const trigger = document.getElementById('ds-fw-trigger')!; | |
| const dropdown = document.getElementById('ds-fw-dropdown')!; | |
| const options = document.querySelectorAll<HTMLButtonElement>('.ds-fw-option'); | |
| function setFramework(fw: string) { | |
| document.documentElement.dataset.framework = fw; | |
| localStorage.setItem(STORAGE_KEY, fw); | |
| options.forEach((opt) => { | |
| const isActive = opt.dataset.frameworkOption === fw; | |
| opt.setAttribute('aria-selected', String(isActive)); | |
| }); | |
| } | |
| function openDropdown() { | |
| dropdown.classList.remove('hidden'); | |
| trigger.setAttribute('aria-expanded', 'true'); | |
| } | |
| function closeDropdown() { | |
| dropdown.classList.add('hidden'); | |
| trigger.setAttribute('aria-expanded', 'false'); | |
| } | |
| // Initialize | |
| const saved = localStorage.getItem(STORAGE_KEY) || 'vue'; | |
| setFramework(saved); | |
| trigger.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| const isOpen = !dropdown.classList.contains('hidden'); | |
| isOpen ? closeDropdown() : openDropdown(); | |
| }); | |
| options.forEach((opt) => { | |
| opt.addEventListener('click', () => { | |
| const fw = opt.dataset.frameworkOption; | |
| if (fw) setFramework(fw); | |
| closeDropdown(); | |
| }); | |
| }); | |
| // Close on outside click | |
| document.addEventListener('click', (e) => { | |
| if (!dropdown.classList.contains('hidden') && !(e.target as HTMLElement).closest('.ds-framework-switcher')) { | |
| closeDropdown(); | |
| } | |
| }); | |
| // Close on Escape | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'Escape' && !dropdown.classList.contains('hidden')) { | |
| closeDropdown(); | |
| trigger.focus(); | |
| } | |
| }); | |
| const trigger = document.getElementById('ds-fw-trigger'); | |
| const dropdown = document.getElementById('ds-fw-dropdown'); | |
| const options = document.querySelectorAll('.ds-fw-option'); | |
| if (!trigger || !dropdown) { | |
| // Required elements are missing; abort initialization. | |
| console.warn('Framework switcher elements not found in the document.'); | |
| } else { | |
| function setFramework(fw) { | |
| const value = String(fw); | |
| document.documentElement.dataset.framework = value; | |
| localStorage.setItem(STORAGE_KEY, value); | |
| options.forEach((opt) => { | |
| const frameworkOption = opt.dataset.frameworkOption; | |
| const isActive = frameworkOption === value; | |
| opt.setAttribute('aria-selected', String(isActive)); | |
| }); | |
| } | |
| function openDropdown() { | |
| dropdown.classList.remove('hidden'); | |
| trigger.setAttribute('aria-expanded', 'true'); | |
| } | |
| function closeDropdown() { | |
| dropdown.classList.add('hidden'); | |
| trigger.setAttribute('aria-expanded', 'false'); | |
| } | |
| // Initialize | |
| const saved = localStorage.getItem(STORAGE_KEY) || 'vue'; | |
| setFramework(saved); | |
| trigger.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| const isOpen = !dropdown.classList.contains('hidden'); | |
| isOpen ? closeDropdown() : openDropdown(); | |
| }); | |
| options.forEach((opt) => { | |
| opt.addEventListener('click', () => { | |
| const fw = opt.dataset.frameworkOption; | |
| if (fw) setFramework(fw); | |
| closeDropdown(); | |
| }); | |
| }); | |
| // Close on outside click | |
| document.addEventListener('click', (e) => { | |
| const target = e.target; | |
| if ( | |
| !dropdown.classList.contains('hidden') && | |
| (!(target instanceof Element) || | |
| !target.closest('.ds-framework-switcher')) | |
| ) { | |
| closeDropdown(); | |
| } | |
| }); | |
| // Close on Escape | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'Escape' && !dropdown.classList.contains('hidden')) { | |
| closeDropdown(); | |
| trigger.focus(); | |
| } | |
| }); | |
| } |
| class DesktopStarlightTOC extends HTMLElement { | ||
| private _current: HTMLAnchorElement | null = null | ||
| private minH: number | ||
| private maxH: number |
There was a problem hiding this comment.
The TOC custom element is defined in a browser <script> but uses TypeScript class property modifiers (private) and type annotations, which are invalid JavaScript and will crash at runtime. Remove TS-only syntax (use JS fields) or mark the script as TypeScript (lang=\"ts\") so Vite transpiles it before shipping to the browser.
| class MobileStarlightTOC extends HTMLElement { | ||
| private _current: HTMLAnchorElement | null = null | ||
| private minH: number | ||
| private maxH: number |
There was a problem hiding this comment.
Same issue as the desktop TOC: this browser script contains TypeScript-only syntax (private + type annotations). This will cause a client-side syntax error and break the mobile TOC behavior. Convert to valid JS or add lang=\"ts\" so it compiles.
| @@ -0,0 +1,35 @@ | |||
| { | |||
| "name": "@destyler-ui/docs", | |||
| "private": "true", | |||
There was a problem hiding this comment.
private must be a boolean in package.json. Using a string can break tooling that validates package metadata. Change this to \"private\": true.
| "private": "true", | |
| "private": true, |
| function initComponentPreviews() { | ||
| document.querySelectorAll('.ds-show-code-btn').forEach((btn) => { | ||
| btn.addEventListener('click', () => { | ||
| const preview = btn.closest('.ds-component-preview') | ||
| if (!preview) return | ||
| const codeContainer = preview.querySelector<HTMLElement>('.ds-code-container') | ||
| if (!codeContainer) return | ||
|
|
||
| const isCollapsed = codeContainer.style.gridTemplateRows === '0fr' | ||
| codeContainer.style.gridTemplateRows = isCollapsed ? '1fr' : '0fr' | ||
| btn.setAttribute('aria-expanded', String(isCollapsed)) | ||
|
|
||
| const label = btn.querySelector('.ds-show-code-label') | ||
| if (label) { | ||
| label.textContent = isCollapsed ? 'Hide Code' : 'Show Code' | ||
| } | ||
| }) | ||
| }) |
There was a problem hiding this comment.
initComponentPreviews() is called on initial load and on astro:after-swap, but it unconditionally adds new event listeners each run. This will cause duplicate handlers (multiple toggles/copies per click) after navigations. Guard initialization per button (e.g., a data-initialized flag), or use event delegation on a stable ancestor.
| <button | ||
| aria-expanded="false" | ||
| aria-label={Astro.locals.t('menuButton.accessibleLabel')} | ||
| aria-controls="starlight__sidebar" | ||
| class="fixed top-[calc((var(--sl-nav-height)-2rem)/2)] end-[var(--sl-nav-pad-x)] z-[var(--sl-z-index-navbar)] flex items-center justify-center size-8 p-0 bg-$sl-color-bg-nav text-$sl-color-gray-3 cursor-pointer transition-colors duration-150 hover:(text-$sl-color-text border-$sl-color-accent) group-aria-expanded:(border-$sl-color-accent text-$sl-color-text) min-[50rem]:hidden" | ||
| > |
There was a problem hiding this comment.
The component toggles aria-expanded on the custom element (starlight-menu-button) but the actual interactive control is the <button>, which stays aria-expanded=\"false\". This makes the control’s expanded state incorrect for assistive tech. Update aria-expanded on the button (and keep the wrapper attribute only if you need it for styling variants).
No description provided.