From 575d68572f0f1407c192cf47d690d5efe800d628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kuna?= <1282324+codecalm@users.noreply.github.com> Date: Mon, 3 Aug 2026 00:55:32 +0200 Subject: [PATCH] Render Modal as real markup; drain scripts via CaptureScript (#2742) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .claude/agents/astro-components.md | 12 +- docs/astro.config.mjs | 2 +- docs/components/DocsLogo.astro | 2 +- docs/components/DocsMenu.astro | 122 +- docs/components/DocsNavbar.astro | 107 +- docs/components/DocsPagination.astro | 173 +- docs/components/DocsToc.astro | 58 +- docs/components/Example.astro | 125 +- docs/layouts/DocsLayout.astro | 29 +- docs/pages/ui/base/markdown/index.astro | 7 +- docs/pages/ui/base/typography.mdx | 2 +- preview/astro.config.mjs | 212 ++- preview/pages/2-step-verification-code.astro | 146 +- preview/pages/2-step-verification.astro | 4 +- preview/pages/all-elements.astro | 1585 +++++++++-------- preview/pages/auth-lock.astro | 8 +- preview/pages/avatars.astro | 315 ++-- preview/pages/badges.astro | 383 ++-- preview/pages/blank.astro | 9 +- preview/pages/buttons.astro | 376 ++-- preview/pages/changelog.astro | 17 +- preview/pages/colorpicker.astro | 55 +- preview/pages/colors.astro | 450 ++--- preview/pages/dashboard-crypto.astro | 623 +++---- preview/pages/email-inbox.astro | 386 ++-- preview/pages/emails.astro | 180 +- preview/pages/flags.astro | 57 +- preview/pages/gallery.astro | 46 +- preview/pages/illustrations.astro | 364 ++-- preview/pages/inline-player.astro | 51 +- preview/pages/job-listing.astro | 271 ++- preview/pages/license.astro | 120 +- preview/pages/lightbox.astro | 40 +- preview/pages/map-fullsize.astro | 55 +- preview/pages/maps.astro | 61 +- preview/pages/markdown.astro | 8 +- preview/pages/marketing/pricing.astro | 28 +- preview/pages/modals.astro | 321 ++-- preview/pages/music.astro | 43 +- preview/pages/navigation.astro | 64 +- preview/pages/onboarding.astro | 240 ++- preview/pages/pay.astro | 199 ++- preview/pages/playground-categories.astro | 634 ++++--- preview/pages/playground-layout.astro | 41 +- preview/pages/playground.astro | 469 +++-- preview/pages/progress.astro | 453 ++--- preview/pages/screenshot.astro | 130 +- preview/pages/sign-in-cover.astro | 47 +- preview/pages/sign-in-illustration.astro | 3 +- preview/pages/sign-in-link.astro | 33 +- preview/pages/signatures.astro | 196 +- preview/pages/sitemap.xml.ts | 7 +- preview/pages/social-icons.astro | 179 +- preview/pages/tags.astro | 197 +- preview/pages/tasks-list.astro | 296 +-- preview/pages/terms-of-service.astro | 11 +- preview/pages/text-features.astro | 135 +- preview/pages/tour.astro | 430 ++--- preview/pages/users.astro | 108 +- preview/pages/wizard.astro | 3 +- shared/components/CaptureModal.astro | 8 + shared/components/CaptureScript.astro | 8 + shared/components/PageScripts.astro | 10 + shared/components/cards/AuthLockCard.astro | 43 +- shared/components/cards/BodyPlaceholder.astro | 4 - shared/components/cards/Card.astro | 16 +- .../components/cards/CardBackgroundIcon.astro | 18 +- shared/components/cards/CardImage.astro | 44 +- shared/components/cards/CardRibbonText.astro | 16 +- shared/components/cards/CardRibbonTop.astro | 18 +- shared/components/cards/CardTabs.astro | 63 +- shared/components/cards/CarouselCard.astro | 158 +- shared/components/cards/Code.astro | 15 +- shared/components/cards/CreditCard.astro | 93 +- shared/components/cards/CrmStats.astro | 92 +- shared/components/cards/EmptyTeam.astro | 29 +- .../components/cards/ForgotPasswordCard.astro | 1 - shared/components/cards/GalleryPhoto.astro | 84 +- shared/components/cards/Icons.astro | 73 +- shared/components/cards/InvoiceCard.astro | 1 - shared/components/cards/PricingCard.astro | 80 +- shared/components/cards/ProfileContact.astro | 52 +- shared/components/cards/ProjectKanban.astro | 77 +- shared/components/cards/ProjectProgress.astro | 79 +- shared/components/cards/RecentActivity.astro | 70 +- shared/components/cards/SignUpCard.astro | 1 - shared/components/cards/SocialTraffic.astro | 66 +- shared/components/cards/TabsCard.astro | 177 +- shared/components/cards/UserCardBg.astro | 47 +- shared/components/cards/UserCardBig.astro | 35 +- shared/components/cards/UserInfo.astro | 86 +- shared/components/cards/UsersList.astro | 169 +- shared/components/cards/UsersList2.astro | 87 +- .../components/cards/UsersListHeaders.astro | 107 +- .../cards/charts/SocialReferrals.astro | 28 +- .../components/cards/music/TracksList.astro | 100 +- .../components/cards/placeholder/Card1.astro | 1 - .../components/cards/placeholder/Card2.astro | 1 - .../components/cards/placeholder/Card3.astro | 1 - .../components/cards/placeholder/Card4.astro | 1 - .../components/cards/placeholder/Card5.astro | 1 - .../components/cards/placeholder/Card6.astro | 49 +- shared/components/forms/FormElements1.astro | 243 +-- shared/components/js/TablerList.astro | 6 +- shared/components/layout/Footer.astro | 111 +- .../layout/HeaderActionsBreadcrumb.astro | 9 +- .../layout/HeaderActionsButtons.astro | 63 +- .../layout/HeaderActionsPhotos.astro | 28 +- shared/components/layout/HeaderUptime.astro | 61 +- shared/components/layout/PageHeader.astro | 2 +- .../marketing/MarketingNavbar.astro | 89 +- .../components/marketing/hero/Browser.astro | 87 +- shared/components/marketing/hero/Side.astro | 129 +- .../marketing/sections/Features.astro | 54 +- .../marketing/sections/Features2.astro | 126 +- .../marketing/sections/Features3.astro | 125 +- .../marketing/sections/Testimonials.astro | 131 +- .../modals/AddTaskModalContent.astro | 68 +- shared/components/modals/CaptureModal.astro | 8 - .../modals/ChangePasswordModalContent.astro | 180 +- .../modals/ConfirmDeleteModalContent.astro | 81 +- shared/components/modals/Modal.astro | 67 +- .../modals/SignatureModalContent.astro | 2 +- shared/components/navbar/Navbar.astro | 25 +- shared/components/navbar/NavbarLogo.astro | 81 +- shared/components/navbar/NavbarMenu.astro | 192 +- shared/components/navbar/NavbarMenuItem.astro | 108 +- shared/components/navbar/NavbarSearch.astro | 20 +- shared/components/navbar/NavbarSide.astro | 106 +- shared/components/navbar/NavbarSideApps.astro | 78 +- .../navbar/NavbarSideNotifications.astro | 183 +- shared/components/navbar/NavbarSideUser.astro | 59 +- shared/components/navbar/Sidebar.astro | 59 +- shared/components/parts/Datagrid.astro | 159 +- shared/components/parts/NavAside.astro | 146 +- shared/components/parts/Tasks.astro | 231 +-- shared/layouts/BaseLayout.astro | 338 ++-- shared/layouts/DefaultLayout.astro | 232 ++- shared/layouts/ErrorLayout.astro | 55 +- shared/layouts/MarketingLayout.astro | 308 ++-- shared/layouts/PayLayout.astro | 46 +- shared/layouts/RedirectLayout.astro | 58 +- shared/layouts/SettingsLayout.astro | 2 +- shared/lib/chart-script.ts | 16 +- shared/lib/code-example.ts | 7 +- shared/lib/date-format.ts | 8 +- shared/lib/docs-children.ts | 2 +- shared/lib/include-args.ts | 9 +- shared/lib/page-modals.ts | 13 +- shared/lib/page-scripts.ts | 19 + shared/lib/render-markdown.ts | 10 +- shared/lib/site.ts | 8 +- shared/ui/ActivityPart.astro | 89 +- shared/ui/AdvancedTable.astro | 345 ++-- shared/ui/Alert.astro | 152 +- shared/ui/Avatar.astro | 120 +- shared/ui/AvatarList.astro | 42 +- shared/ui/AvatarUpload.astro | 18 +- shared/ui/Badge.astro | 68 +- shared/ui/Breadcrumb.astro | 69 +- shared/ui/Button.astro | 167 +- shared/ui/Chart.astro | 32 +- shared/ui/ChartRadial.astro | 114 +- shared/ui/ChartSparkline.astro | 25 +- shared/ui/Colorpicker.astro | 73 +- shared/ui/Datepicker.astro | 44 +- shared/ui/DropdownDays.astro | 1 - shared/ui/DropdownMenu.astro | 229 ++- shared/ui/DropdownMenuAll.astro | 68 +- shared/ui/Dropzone.astro | 56 +- shared/ui/Empty.astro | 82 +- shared/ui/Flag.astro | 1 - shared/ui/Fullcalendar.astro | 87 +- shared/ui/Illustration.astro | 28 +- shared/ui/InlinePlayer.astro | 29 +- shared/ui/Map.astro | 52 +- shared/ui/MapVector.astro | 42 +- shared/ui/NavSegmented.astro | 128 +- shared/ui/Pagination.astro | 178 +- shared/ui/Payment.astro | 22 +- shared/ui/Photo.astro | 6 +- shared/ui/Progress.astro | 1 - shared/ui/ProgressBg.astro | 43 +- shared/ui/ProgressDescription.astro | 41 +- shared/ui/ProgressSteps.astro | 1 - shared/ui/Range.astro | 38 +- shared/ui/Rating.astro | 83 +- shared/ui/Select.astro | 58 +- shared/ui/Signature.astro | 69 +- shared/ui/Spinner.astro | 38 +- shared/ui/Steps.astro | 7 +- shared/ui/SwitchIcon.astro | 44 +- shared/ui/Table.astro | 202 ++- shared/ui/ThemeSettings.astro | 270 +-- shared/ui/Timeline.astro | 195 +- shared/ui/Toast.astro | 99 +- shared/ui/Trending.astro | 20 +- shared/ui/Wysiwyg.astro | 5 +- shared/ui/form/Check.astro | 97 +- shared/ui/form/InputMask.astro | 3 +- 200 files changed, 10670 insertions(+), 10096 deletions(-) create mode 100644 shared/components/CaptureModal.astro create mode 100644 shared/components/CaptureScript.astro create mode 100644 shared/components/PageScripts.astro delete mode 100644 shared/components/modals/CaptureModal.astro create mode 100644 shared/lib/page-scripts.ts diff --git a/.claude/agents/astro-components.md b/.claude/agents/astro-components.md index aa0551a1e..f68084fc0 100644 --- a/.claude/agents/astro-components.md +++ b/.claude/agents/astro-components.md @@ -65,16 +65,14 @@ additively instead of creating parallel variants. ## Page scripts and modals -- Register per-page scripts with `addPageScript()` and modals with - `addPageModal()` (`@shared/lib/page-scripts.ts` / `page-modals.ts`). +- Capture markup with `CaptureScript` / `CaptureModal` (HTML in the slot, not + template strings). They register via `addPageScript()` / `addPageModal()` + (`@shared/lib/page-scripts.ts` / `page-modals.ts`). + Wrap at the call site, e.g. ``. Registration MUST be synchronous in the component frontmatter (before the first `await`) — Astro renders siblings concurrently, and a registration after `await Astro.slots.render()` loses the race against the drain in - `PageScripts`/`PageModals` (emitted by `BaseLayout`). -- Script-emitting components also render ``. - Its behavior is chosen per package by the vite define - `import.meta.env.INLINE_PAGE_SCRIPTS`: preview drains registered scripts at - the end of the page; docs inlines them next to the example. + `PageScripts`/`PageModals` (emitted by `BaseLayout` / `DocsLayout`). - Third-party page libraries: list names in the layout's `pageLibs` prop — resolved via `@tabler/core/libs.json` (a full `http` URL in there is emitted verbatim; `head: true` libs go into ``). diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 63838f477..3e320e5f8 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -34,7 +34,7 @@ export default defineConfig({ }, integrations: [mdx()], markdown: { - // markdown-it in Eleventy did not produce typographic quotes — neither do we + // No typographic quote rewriting. processor: satteri({ features: { smartPunctuation: false } }), shikiConfig: { theme: 'github-dark', diff --git a/docs/components/DocsLogo.astro b/docs/components/DocsLogo.astro index 717511934..dd9eab38f 100644 --- a/docs/components/DocsLogo.astro +++ b/docs/components/DocsLogo.astro @@ -1,5 +1,5 @@ --- -// Equivalent of docs/logo.html — the Tabler logo with wordmark (120x32), pasted 1:1. +// Tabler logo with wordmark (120x32). --- diff --git a/docs/components/DocsMenu.astro b/docs/components/DocsMenu.astro index 81f182dfc..b4f3bf89f 100644 --- a/docs/components/DocsMenu.astro +++ b/docs/components/DocsMenu.astro @@ -1,78 +1,86 @@ --- -// Equivalent of docs/menu.html. In Eleventy the menu is built from `collections.docs | -// collection-tree` (the tree of content/**/*.md files of the @tabler/docs app); in the PoC -// the tree is frozen in shared/data/docs.json (extracted from the reference -// development docs build). - -import Subheader from '@ui/Subheader.astro' -import docs from '@data/docs.json' -import { pathSlug as slug } from '@shared/lib/string-format' +// Menu tree from shared/data/docs.json. +import Subheader from '@ui/Subheader.astro'; +import docs from '@data/docs.json'; +import { pathSlug as slug } from '@shared/lib/string-format'; interface MenuLeaf { - title: string - url: string + title: string; + url: string; } interface MenuGroup extends MenuLeaf { - children?: MenuLeaf[] + children?: MenuLeaf[]; } interface MenuSection { - title: string - children?: MenuGroup[] + title: string; + children?: MenuGroup[]; } interface Props { - /** Page URL in the docs namespace (equivalent of page.url), e.g. "/ui/components/alert/". */ - url: string + /** Page URL in the docs namespace (equivalent of page.url), e.g. "/ui/components/alert/". */ + url: string; } -const { url } = Astro.props -const menu = docs.menu as MenuSection[] +const { url } = Astro.props; +const menu = docs.menu as MenuSection[]; ---
- {level1.title} - {level1.children && level1.children.length > 0 && ( - + )} +
+ )) + } diff --git a/docs/components/DocsNavbar.astro b/docs/components/DocsNavbar.astro index c7798c012..416df57c2 100644 --- a/docs/components/DocsNavbar.astro +++ b/docs/components/DocsNavbar.astro @@ -1,63 +1,62 @@ --- -// Equivalent of docs/navbar.html. -import Icon from '@ui/Icon.astro' -import DocsLogo from './DocsLogo.astro' -import { site } from '@shared/lib/site' +import Icon from '@ui/Icon.astro'; +import DocsLogo from './DocsLogo.astro'; +import { site } from '@shared/lib/site'; // Eventually belongs in src/lib/site.ts (out of scope for this task). -const previewUrl = 'https://preview.tabler.io' -const changelogUrl = 'https://tabler.io/changelog' +const previewUrl = 'https://preview.tabler.io'; +const changelogUrl = 'https://tabler.io/changelog'; --- diff --git a/docs/components/DocsPagination.astro b/docs/components/DocsPagination.astro index 29b61733b..162915960 100644 --- a/docs/components/DocsPagination.astro +++ b/docs/components/DocsPagination.astro @@ -1,114 +1,119 @@ --- -// Equivalent of docs/pagination.html: child-page cards or next/prev siblings. -import DocsCard from './DocsCard.astro' -import docs from '@data/docs.json' -import Icon from '@ui/Icon.astro' -import { getDocsChildren } from '@shared/lib/docs-children' -import type { DocsChildPage } from '@shared/lib/docs-children' +import DocsCard from './DocsCard.astro'; +import docs from '@data/docs.json'; +import Icon from '@ui/Icon.astro'; +import { getDocsChildren } from '@shared/lib/docs-children'; +import type { DocsChildPage } from '@shared/lib/docs-children'; interface MenuLeaf { - title: string - url: string + title: string; + url: string; } interface MenuNode { - title: string - url?: string - children?: MenuNode[] + title: string; + url?: string; + children?: MenuNode[]; } interface Props { - /** Page URL in the docs namespace (equivalent of page.url), e.g. "/ui/components/alert/". */ - url: string + /** Page URL in the docs namespace (equivalent of page.url), e.g. "/ui/components/alert/". */ + url: string; } -const { url } = Astro.props +const { url } = Astro.props; -const children = getDocsChildren(url) +const children = getDocsChildren(url); type Pagination = { - prev: MenuLeaf | null - next: MenuLeaf | null - found: boolean -} + prev: MenuLeaf | null; + next: MenuLeaf | null; + found: boolean; +}; function findPage(nodes: MenuNode[]): Pagination | null { - const index = nodes.findIndex((item) => item.url === url) + const index = nodes.findIndex((item) => item.url === url); - if (index !== -1) { - const previousNode = index > 0 ? nodes[index - 1] : null - const nextNode = index < nodes.length - 1 ? nodes[index + 1] : null + if (index !== -1) { + const previousNode = index > 0 ? nodes[index - 1] : null; + const nextNode = index < nodes.length - 1 ? nodes[index + 1] : null; - return { - prev: previousNode?.url ? { title: previousNode.title, url: previousNode.url } : null, - next: nextNode?.url ? { title: nextNode.title, url: nextNode.url } : null, - found: true, - } - } + return { + prev: previousNode?.url ? { title: previousNode.title, url: previousNode.url } : null, + next: nextNode?.url ? { title: nextNode.title, url: nextNode.url } : null, + found: true, + }; + } - for (const node of nodes) { - const result = node.children ? findPage(node.children) : null - if (result) return result - } + for (const node of nodes) { + const result = node.children ? findPage(node.children) : null; + if (result) return result; + } - return null + return null; } const { prev, next, found } = findPage(docs.menu as MenuNode[]) ?? { - prev: null, - next: null, - found: false, -} + prev: null, + next: null, + found: false, +}; --- -{ - children.length > 0 && ( -
-
- {children.map((child: DocsChildPage) => ( - - ))} -
-
- ) + +{ + children.length > 0 && ( +
+
+ {children.map((child: DocsChildPage) => ( + + ))} +
+
+ ) } { - children.length === 0 && found && ( -
- -
- ) + children.length === 0 && found && ( +
+ +
+ ) } diff --git a/docs/components/DocsToc.astro b/docs/components/DocsToc.astro index ed549920e..62899f5d4 100644 --- a/docs/components/DocsToc.astro +++ b/docs/components/DocsToc.astro @@ -1,46 +1,46 @@ --- -// Equivalent of docs/toc.html. In Eleventy the TOC is computed by the `content | toc` -// filter (h2/h3 headings from the markdown content, excluding ... blocks); -// here it comes in as a prop from the consuming page. +// TOC comes in as a prop from the consuming page (h2/h3 headings from the +// markdown content, excluding ... blocks). export interface TocItem { - /** 2 or 3 (h2/h3); h3 gets the ms-3 indent */ - level: number - text: string - /** anchor without '#', e.g. "default-markup" */ - id: string + /** 2 or 3 (h2/h3); h3 gets the ms-3 indent */ + level: number; + text: string; + /** anchor without '#', e.g. "default-markup" */ + id: string; } interface Props { - toc?: TocItem[] + toc?: TocItem[]; } -const { toc = [] } = Astro.props +const { toc = [] } = Astro.props; -// Equivalent of `illustrations | size` (shared/data/illustrations.json has 100 entries). -const illustrationsCount = 100 +// shared/data/illustrations.json has 100 entries. +const illustrationsCount = 100; --- -{ - toc.length > 0 && ( - -

Table of Contents

- -
- ) + +{ + toc.length > 0 && ( + +

Table of Contents

+ +
+ ) } -
- +
+ -

{illustrationsCount} sleek illustrations for your startup's visual identity.

-
+

{illustrationsCount} sleek illustrations for your startup's visual identity.

+
diff --git a/docs/components/Example.astro b/docs/components/Example.astro index 6b170f8c0..a48fb2e56 100644 --- a/docs/components/Example.astro +++ b/docs/components/Example.astro @@ -1,68 +1,97 @@ --- -// copy button with data-clipboard-text (equivalent of escape_attribute). -// The example code comes from the `html` prop or from the rendered slot. -import Icon from '@ui/Icon.astro' -import { beautifyHtml, highlightCode, removeHref } from '@shared/lib/code-example' +// Example code from the `html` prop or the rendered slot; copy button uses data-clipboard-text. +import Icon from '@ui/Icon.astro'; +import { beautifyHtml, highlightCode, removeHref } from '@shared/lib/code-example'; interface Props { - /** raw HTML of the example; when absent — the slot is rendered */ - html?: string - /** code shown in the panel instead of html (the code param in Liquid) */ - code?: string - raw?: boolean - overflow?: string - bg?: string - class?: string - height?: string - column?: boolean - centered?: boolean - vertical?: boolean - columnFullWidth?: boolean - hideCode?: boolean - codeOnly?: boolean + /** raw HTML of the example; when absent — the slot is rendered */ + html?: string; + /** code shown in the panel instead of html */ + code?: string; + raw?: boolean; + overflow?: string; + bg?: string; + class?: string; + height?: string; + column?: boolean; + centered?: boolean; + vertical?: boolean; + columnFullWidth?: boolean; + hideCode?: boolean; + codeOnly?: boolean; } -const { html: htmlProp, code, raw, overflow = 'auto', bg, class: className, height, column, centered, vertical, columnFullWidth, hideCode, codeOnly } = Astro.props +const { + html: htmlProp, + code, + raw, + overflow = 'auto', + bg, + class: className, + height, + column, + centered, + vertical, + columnFullWidth, + hideCode, + codeOnly, +} = Astro.props; -// equivalent of {% removeemptylines %} around include output (e.g. ui/alert.html) -let html = (htmlProp ?? (await Astro.slots.render('default'))).replace(/^\s*[\r\n]/gm, '') +// Strip empty lines from the example HTML. +let html = (htmlProp ?? (await Astro.slots.render('default'))).replace(/^\s*[\r\n]/gm, ''); // JSX reserializes the slot markup: empty SVG elements get explicit closing // tags, and apostrophes in text are escaped. We restore the source form so the // code in the panel looks like hand-written HTML. -html = html.replace(/<(path|circle|line|polyline|polygon|rect|ellipse)([^>]*?)\s*><\/\1>/g, '<$1$2 />').replace(/'/g, "'") +html = html + .replace(/<(path|circle|line|polyline|polygon|rect|ellipse)([^>]*?)\s*><\/\1>/g, '<$1$2 />') + .replace(/'/g, "'"); -const exampleClasses = ['example fs-base border rounded my-5', !raw && 'd-flex flex-wrap justify-content-center', `overflow-${overflow}`, 'position-relative', bg ? `bg-${bg}` : 'bg-pattern-rectangles', className] +const exampleClasses = [ + 'example fs-base border rounded my-5', + !raw && 'd-flex flex-wrap justify-content-center', + `overflow-${overflow}`, + 'position-relative', + bg ? `bg-${bg}` : 'bg-pattern-rectangles', + className, +] -const innerClasses = ['p-6 w-full', column && 'd-flex gap-3 flex-column', !column && centered && `d-flex flex-fill flex-wrap gap-2 justify-content-center${vertical ? ' align-items-center flex-column' : ' justify-content-center'}`, columnFullWidth && 'd-flex flex-fill flex-column gap-2'] +const innerClasses = [ + 'p-6 w-full', + column && 'd-flex gap-3 flex-column', + !column && centered && `d-flex flex-fill flex-wrap gap-2 justify-content-center${vertical ? ' align-items-center flex-column' : ' justify-content-center'}`, + columnFullWidth && 'd-flex flex-fill flex-column gap-2', +] -const highlighted = hideCode ? '' : await highlightCode(beautifyHtml(code ?? html), 'html') +const highlighted = hideCode ? '' : await highlightCode(beautifyHtml(code ?? html), 'html'); --- -{ - !codeOnly && ( -
- {raw ? ( - - ) : ( -
- -
- )} -
- ) + +{ + !codeOnly && ( +
+ { + raw ? ( + + ) : ( +
+ +
+ ) + } +
+ ) } { - !hideCode && ( -
- {/* Astro escapes the attribute value itself (equivalent of escape_attribute) */} - - - - - -
- ) + !hideCode && ( +
+ + + + + +
+ ) } diff --git a/docs/layouts/DocsLayout.astro b/docs/layouts/DocsLayout.astro index d6a64fac3..5595e56d1 100644 --- a/docs/layouts/DocsLayout.astro +++ b/docs/layouts/DocsLayout.astro @@ -1,5 +1,5 @@ --- -// Docs layout — port of shared/layouts/docs/default.html (the @tabler/docs app). +// Docs layout for the @tabler/docs app. // Standalone layout with its own ; does NOT use BaseLayout.astro. // Development uses the unminified assets; production additionally emits the SEO metadata. import { site } from '@shared/lib/site'; @@ -12,6 +12,7 @@ import DocsToc from '@components/DocsToc.astro'; import type { TocItem } from '@components/DocsToc.astro'; import DocsPagination from '@components/DocsPagination.astro'; import Prose from '@ui/Prose.astro'; +import PageScripts from '@shared/components/PageScripts.astro'; interface Props { /** front matter: the page's h1 heading */ @@ -26,16 +27,16 @@ interface Props { seoDescription?: string; /** front matter `added-in`: renders the "Added in X" badge next to the h1 */ addedIn?: string; - /** table of contents (h2/h3 from the markdown content) — see DocsToc.astro */ + /** Table of contents (h2/h3 from the markdown content) — see DocsToc.astro */ toc?: TocItem[]; /** - * Page URL in the docs namespace (equivalent of Eleventy's page.url), e.g. - * "/ui/components/alert/". By default derived from Astro.url.pathname by - * stripping the /docs prefix. Drives the active menu entry, pagination, - * the "Edit this page" link, and the relative favicon path. + * Page URL in the docs namespace, e.g. "/ui/components/alert/". + * By default derived from Astro.url.pathname by stripping the /docs prefix. + * Drives the active menu entry, pagination, the "Edit this page" link, and + * the relative favicon path. */ url?: string; - /** front matter `docs-libs`: extra libraries from src/data/libs.json (css+js) */ + /** Extra libraries from libs.json (css+js) */ docsLibs?: string[]; /** hides child and previous/next page navigation */ hidePagination?: boolean; @@ -57,7 +58,7 @@ const { } = Astro.props; const environment = process.env.NODE_ENV || 'production'; -// URL in the docs namespace: /docs/ui/components/alert(.html) -> /ui/components/alert/ +// Strip /docs prefix (and optional .html) for the docs namespace URL. const docsUrl = Astro.props.url ?? (() => { @@ -80,7 +81,7 @@ const metaDescription = seoDescription ?? description; const siteName = pageSection ? `Tabler ${pageSection} Documentation` : 'Tabler Documentation'; const canonicalUrl = new URL(docsUrl, Astro.site ?? site.docsUrl).href; -// Equivalent of `{{ page | relative }}`: for /ui/components/alert/ -> "../../.." +// Relative asset base: for /ui/components/alert/ -> "../../.." const depth = docsUrl.split('/').filter(Boolean).length; const relative = depth === 0 ? '.' : '../'.repeat(depth).slice(0, -1); @@ -88,8 +89,8 @@ const relative = depth === 0 ? '.' : '../'.repeat(depth).slice(0, -1); // MDX layout; fall back to the flat-page convention (docs/pages/.mdx). const editFilePath = editPath ?? `docs/pages${docsUrl.replace(/\/$/, '')}.mdx`; -// JS/CSS libraries as in Eleventy: docs-libs + clipboard (always included). -// In dev file names stay unchanged (e.g. dist/clipboard.min.js keeps .min — the reference has it so). +// docs-libs + clipboard (always included). In dev, file names stay unchanged +// (e.g. dist/clipboard.min.js keeps .min). type Lib = { npm?: string; js?: string[]; css?: string[] }; const libEntries = Object.entries(libs as Record); const libUrl = (lib: Lib, file: string) => @@ -234,7 +235,6 @@ const docsLinks = docs.links as DocsLink[]; { addedIn && (
- {/* equivalent of ui/badge.html color="primary" light=true */} Added in {addedIn}
) @@ -243,8 +243,7 @@ const docsLinks = docs.links as DocsLink[];

{summary}

- {/* markdown content; equivalent of {{ content | headings-id }} — the consuming - page must supply headings with ids (MDX does this by default) */} + {/* Page must supply headings with ids (MDX does this by default). */} {!hidePagination && } @@ -340,6 +339,8 @@ const docsLinks = docs.links as DocsLink[]; + + diff --git a/docs/pages/ui/base/markdown/index.astro b/docs/pages/ui/base/markdown/index.astro index c11cd70a1..0aab22c30 100644 --- a/docs/pages/ui/base/markdown/index.astro +++ b/docs/pages/ui/base/markdown/index.astro @@ -1,10 +1,5 @@ --- -// docs/content/ui/base/markdown.md sets `layout: redirect` + `redirect.to: ui/base/prose/`, -// but shared/includes/redirect.html reads `page.redirect.to` — undefined in Eleventy — -// so the include gets an empty url and the target collapses to `{{ page | relative }}`. -// For this 3-deep page that is "../../..", which is exactly what the reference -// build (ui/base/markdown/index.html) redirects to. -import RedirectLayout from '@shared/layouts/RedirectLayout.astro' +import RedirectLayout from '@shared/layouts/RedirectLayout.astro'; --- diff --git a/docs/pages/ui/base/typography.mdx b/docs/pages/ui/base/typography.mdx index 9fb97590d..b523b42a2 100644 --- a/docs/pages/ui/base/typography.mdx +++ b/docs/pages/ui/base/typography.mdx @@ -111,7 +111,7 @@ To edit settings, press ctrl + , or ctrl +

Hello World

Lorem ipsum[1] dolor sit amet, consectetur adipiscing elit. Nulla accumsan, metus ultrices eleifend gravida, nulla nunc varius lectus, nec rutrum justo nibh eu lectus. Ut vulputate semper dui. Fusce erat odio, sollicitudin vel erat vel, interdum mattis neque. Subscript works as well!

Second level

Curabitur accumsan turpis pharetra augue tincidunt blandit. Quisque condimentum maximus mi, sit amet commodo arcu rutrum id. Proin pretium urna vel cursus venenatis. Suspendisse potenti. Etiam mattis sem rhoncus lacus dapibus facilisis. Donec at dignissim dui. Ut et neque nisl.

  • In fermentum leo eu lectus mollis, quis dictum mi aliquet.
  • Morbi eu nulla lobortis, lobortis est in, fringilla felis.
  • Aliquam nec felis in sapien venenatis viverra fermentum nec lectus.
  • Ut non enim metus.
diff --git a/preview/astro.config.mjs b/preview/astro.config.mjs index cff5a5141..be8fbdb74 100644 --- a/preview/astro.config.mjs +++ b/preview/astro.config.mjs @@ -1,120 +1,112 @@ // @ts-check -import { defineConfig } from 'astro/config' -import mdx from '@astrojs/mdx' -import { satteri } from '@astrojs/markdown-satteri' -import beautify from 'js-beautify' -import { execFileSync } from 'node:child_process' -import { devNull } from 'node:os' -import { fileURLToPath } from 'node:url' +import { defineConfig } from 'astro/config'; +import mdx from '@astrojs/mdx'; +import { satteri } from '@astrojs/markdown-satteri'; +import beautify from 'js-beautify'; +import { execFileSync } from 'node:child_process'; +import { devNull } from 'node:os'; +import { fileURLToPath } from 'node:url'; /** - * Equivalent of the Eleventy "html-prettify" step (@tabler/preview): the - * generated HTML is the product (users copy it 1:1), so after the build we - * format it with prettier per .prettierrc. + * After build, format page HTML with prettier (users copy it 1:1). * @returns {import('astro').AstroIntegration} */ function prettifyHtml() { - return { - name: 'prettify-html', - hooks: { - 'astro:build:done': async ({ dir, logger }) => { - const outDir = fileURLToPath(dir) - // dist/preview/ and dist/dist/ are copy-assets.mjs's copies of public/{preview,dist} - // (demo css/js and @tabler/core's dist, including vendored libs) — not pages, and - // some vendored libs ship their own malformed docs/*.html that trips the parser below. - /** @param {string} file */ - const isVendorCopy = (file) => file.includes(`${outDir}preview/`) || file.includes(`${outDir}dist/`) - // Astro appends "overflow-x: auto" to the shiki
 style — the
-        // Eleventy pipeline doesn't have it and HTML is the product: restore 1:1.
-        const { globSync } = await import('node:fs')
-        const { readFileSync, writeFileSync } = await import('node:fs')
-        for (const file of globSync(`${outDir}**/*.html`, { exclude: isVendorCopy })) {
-          const content = readFileSync(file, 'utf8')
-          const cleaned = content.replaceAll('; overflow-x: auto;', '')
-          if (cleaned !== content) writeFileSync(file, cleaned)
-        }
-        execFileSync(
-          'npx',
-          [
-            'prettier',
-            '--write',
-            '--parser',
-            'html',
-            // Prettier's default ignore-path is [.gitignore, .prettierignore], and both
-            // list "dist" (needed elsewhere so normal lint/format passes skip build
-            // output) — that silently no-ops this pass on the very directory it targets.
-            // Point at devNull to opt this one deliberate pass out of those ignores.
-            '--ignore-path',
-            devNull,
-            `${outDir}**/*.html`,
-            `!${outDir}preview/**`,
-            `!${outDir}dist/**`,
-          ],
-          { stdio: 'inherit' },
-        )
-        logger.info('HTML formatted with prettier')
-      },
-    },
-  }
+	return {
+		name: 'prettify-html',
+		hooks: {
+			'astro:build:done': async ({ dir, logger }) => {
+				const outDir = fileURLToPath(dir);
+				// dist/preview/ and dist/dist/ are copy-assets.mjs's copies of public/{preview,dist}
+				// (demo css/js and @tabler/core's dist, including vendored libs) — not pages, and
+				// some vendored libs ship their own malformed docs/*.html that trips the parser below.
+				/** @param {string} file */
+				const isVendorCopy = (file) => file.includes(`${outDir}preview/`) || file.includes(`${outDir}dist/`);
+				// Strip Astro's extra "overflow-x: auto" on shiki 
 styles.
+				const { globSync } = await import('node:fs');
+				const { readFileSync, writeFileSync } = await import('node:fs');
+				for (const file of globSync(`${outDir}**/*.html`, { exclude: isVendorCopy })) {
+					const content = readFileSync(file, 'utf8');
+					const cleaned = content.replaceAll('; overflow-x: auto;', '');
+					if (cleaned !== content) writeFileSync(file, cleaned);
+				}
+				execFileSync(
+					'npx',
+					[
+						'prettier',
+						'--write',
+						'--parser',
+						'html',
+						// Prettier's default ignore-path is [.gitignore, .prettierignore], and both
+						// list "dist" (needed elsewhere so normal lint/format passes skip build
+						// output) — that silently no-ops this pass on the very directory it targets.
+						// Point at devNull to opt this one deliberate pass out of those ignores.
+						'--ignore-path',
+						devNull,
+						`${outDir}**/*.html`,
+						`!${outDir}preview/**`,
+						`!${outDir}dist/**`,
+					],
+					{ stdio: 'inherit' },
+				);
+				logger.info('HTML formatted with prettier');
+			},
+		},
+	};
 }
 
 // https://astro.build/config
 export default defineConfig({
-  // pages live at the package root (./pages) — all components/lib/data are
-  // shared (see the @shared alias)
-  srcDir: '.',
-  server: {
-    port: 3000,
-    // bind on all interfaces so the dev server is reachable from Docker
-    // port mappings and other devices on the local network
-    host: true,
-  },
-  vite: {
-    resolve: {
-      alias: {
-        // demo data lives in the monorepo's shared/data — single source of
-        // truth shared with the Eleventy packages (no copies in src/data)
-        '@data': fileURLToPath(new URL('../shared/data', import.meta.url)),
-        // Astro components/lib shared with docs (single source of truth)
-        '@shared': fileURLToPath(new URL('../shared', import.meta.url)),
-        '@ui': fileURLToPath(new URL('../shared/ui', import.meta.url)),
-        '@components': fileURLToPath(new URL('../shared/components', import.meta.url)),
-        // this package's pages dir — used by @shared/lib/docs-children's glob
-        '@pages': fileURLToPath(new URL('./pages', import.meta.url)),
-      },
-    },
-  },
-  build: {
-    // emit sign-in.html instead of sign-in/index.html — matches the Eleventy
-    // preview package layout, where the HTML files are the distributed product
-    format: 'file',
-  },
-  // Do not collapse whitespace in the output — the HTML must stay readable
-  // (like the Eleventy build); prettier finalizes formatting after the build.
-  compressHTML: false,
-  integrations: [mdx(), prettifyHtml()],
-  markdown: {
-    // markdown-it in Eleventy does not produce typographic quotes — neither do we
-    processor: satteri({ features: { smartPunctuation: false } }),
-    shikiConfig: {
-      theme: 'github-dark',
-      transformers: [
-        {
-          // The Eleventy docs pipeline beautifies html fences before highlighting
-          preprocess(code) {
-            if (this.options.lang === 'html') {
-              return beautify.html(code, { indent_size: 2, wrap_line_length: 80 })
-            }
-          },
-          // Eleventy docs emits raw shiki output: 
.
-          // Astro adds its own astro-code class and data-language — restore the
-          // exact markdown-it + shiki pipeline markup.
-          pre(node) {
-            node.properties.class = 'shiki github-dark'
-            delete node.properties.dataLanguage
-          },
-        },
-      ],
-    },
-  },
-})
+	// pages live at the package root (./pages) — all components/lib/data are
+	// shared (see the @shared alias)
+	srcDir: '.',
+	server: {
+		port: 3000,
+		// bind on all interfaces so the dev server is reachable from Docker
+		// port mappings and other devices on the local network
+		host: true,
+	},
+	vite: {
+		resolve: {
+			alias: {
+				// Demo data in shared/data (single source of truth).
+				'@data': fileURLToPath(new URL('../shared/data', import.meta.url)),
+				// Components/lib shared with docs.
+				'@shared': fileURLToPath(new URL('../shared', import.meta.url)),
+				'@ui': fileURLToPath(new URL('../shared/ui', import.meta.url)),
+				'@components': fileURLToPath(new URL('../shared/components', import.meta.url)),
+				// Used by @shared/lib/docs-children's glob.
+				'@pages': fileURLToPath(new URL('./pages', import.meta.url)),
+			},
+		},
+	},
+	build: {
+		// Emit sign-in.html instead of sign-in/index.html (HTML is the product).
+		format: 'file',
+	},
+	// Keep readable HTML; prettier formats after the build.
+	compressHTML: false,
+	integrations: [mdx(), prettifyHtml()],
+	markdown: {
+		// No typographic quote rewriting.
+		processor: satteri({ features: { smartPunctuation: false } }),
+		shikiConfig: {
+			theme: 'github-dark',
+			transformers: [
+				{
+					// Beautify html fences before highlighting.
+					preprocess(code) {
+						if (this.options.lang === 'html') {
+							return beautify.html(code, { indent_size: 2, wrap_line_length: 80 });
+						}
+					},
+					// Keep shiki classes only (drop Astro's astro-code / data-language).
+					pre(node) {
+						node.properties.class = 'shiki github-dark';
+						delete node.properties.dataLanguage;
+					},
+				},
+			],
+		},
+	},
+});
diff --git a/preview/pages/2-step-verification-code.astro b/preview/pages/2-step-verification-code.astro
index 81d10d78b..735484d8a 100644
--- a/preview/pages/2-step-verification-code.astro
+++ b/preview/pages/2-step-verification-code.astro
@@ -1,79 +1,93 @@
 ---
-import FormFooter from '@ui/FormFooter.astro'
-import SingleLayout from '@shared/layouts/SingleLayout.astro'
-import Button from '@ui/Button.astro'
-import ButtonList from '@ui/ButtonList.astro'
+import FormFooter from '@ui/FormFooter.astro';
+import SingleLayout from '@shared/layouts/SingleLayout.astro';
+import Button from '@ui/Button.astro';
+import ButtonList from '@ui/ButtonList.astro';
+import CaptureScript from '@shared/components/CaptureScript.astro';
 ---
 
 
-  
-
-

Authenticate Your Account

+ +
+

Authenticate Your Account

-

- Please confirm your account by entering the authorization code sent to +1 856-672-8552. -

+

+ Please confirm your account by entering the authorization code sent to +1 856-672-8552. +

-
-
- { - Array.from({ length: 2 }).map(() => ( -
-
- {Array.from({ length: 3 }).map(() => ( -
- -
- ))} -
-
- )) - } -
-
+
+
+ { + Array.from({ length: 2 }).map(() => ( +
+
+ {Array.from({ length: 3 }).map(() => ( +
+ +
+ ))} +
+
+ )) + } +
+
-
- -
+
+ +
- - -
- + + +
+ - - - + input.addEventListener('keydown', (e) => { + const target = e.target; + // If the input field is empty and Backspace is pressed, and there is a previous input field, focus it + if (target.value.length === 0 && e.key === 'Backspace' && i > 0) { + inputs[i - 1].focus(); + } + }); + }); + + + -
- It may take a minute to receive your code. Haven't received it? Resend a new code. -
+
+ It may take a minute to receive your code. Haven't received it? Resend a new code. +
diff --git a/preview/pages/2-step-verification.astro b/preview/pages/2-step-verification.astro index 7f572f886..238268ae7 100644 --- a/preview/pages/2-step-verification.astro +++ b/preview/pages/2-step-verification.astro @@ -1,7 +1,5 @@ --- -// Liquid: {% if country.code == 'US' %} selected{% endif %} — flags.json entries -// have no `code` property, so the reference renders value="" and never `selected`. -// We mirror that output exactly. +// flags.json entries have no `code` property — value="" and never `selected`. import FormFooter from '@ui/FormFooter.astro' import SingleLayout from '@shared/layouts/SingleLayout.astro' diff --git a/preview/pages/all-elements.astro b/preview/pages/all-elements.astro index dd637f64f..ecbb8349e 100644 --- a/preview/pages/all-elements.astro +++ b/preview/pages/all-elements.astro @@ -1,835 +1,870 @@ --- + // Code-block content lives in the frontmatter so Astro does not try to evaluate // its `${name}` / `{ }` as expressions; injected verbatim via set:html. -import ButtonGroup from '@ui/ButtonGroup.astro' -import DefaultLayout from '@shared/layouts/DefaultLayout.astro' -import Button from '@ui/Button.astro' -import ButtonList from '@ui/ButtonList.astro' -import Card from '@ui/Card.astro' -import CardHeader from '@ui/CardHeader.astro' -import CardBody from '@ui/CardBody.astro' -import CardFooter from '@ui/CardFooter.astro' -import Alert from '@ui/Alert.astro' -import Badge from '@ui/Badge.astro' -import Progress from '@ui/Progress.astro' -import Select from '@ui/Select.astro' -import Check from '@ui/form/Check.astro' -import FormGroup from '@ui/FormGroup.astro' -import Nav from '@ui/Nav.astro' -import Breadcrumb from '@ui/Breadcrumb.astro' -import Pagination from '@ui/Pagination.astro' -import Avatar from '@ui/Avatar.astro' -import Icon from '@ui/Icon.astro' -import Dropdown from '@ui/Dropdown.astro' -import Accordion from '@ui/Accordion.astro' -import Spinner from '@ui/Spinner.astro' -import Rating from '@ui/Rating.astro' -import Steps from '@ui/Steps.astro' -import StatusDot from '@ui/StatusDot.astro' -import Toast from '@ui/Toast.astro' -import InputIcon from '@ui/form/InputIcon.astro' -import InputGroup from '@ui/InputGroup.astro' -import Range from '@ui/Range.astro' -import Tag from '@ui/Tag.astro' -import Ribbon from '@ui/Ribbon.astro' -import Flag from '@ui/Flag.astro' -import Payment from '@ui/Payment.astro' -import Timeline from '@ui/Timeline.astro' -import Empty from '@ui/Empty.astro' -import NavSegmented from '@ui/NavSegmented.astro' -import ListGroup from '@ui/ListGroup.astro' -import ListGroupItem from '@ui/ListGroupItem.astro' +import ButtonGroup from '@ui/ButtonGroup.astro'; +import DefaultLayout from '@shared/layouts/DefaultLayout.astro'; +import Button from '@ui/Button.astro'; +import ButtonList from '@ui/ButtonList.astro'; +import Card from '@ui/Card.astro'; +import CardHeader from '@ui/CardHeader.astro'; +import CardBody from '@ui/CardBody.astro'; +import CardFooter from '@ui/CardFooter.astro'; +import Alert from '@ui/Alert.astro'; +import Badge from '@ui/Badge.astro'; +import Progress from '@ui/Progress.astro'; +import Select from '@ui/Select.astro'; +import Check from '@ui/form/Check.astro'; +import FormGroup from '@ui/FormGroup.astro'; +import Nav from '@ui/Nav.astro'; +import Breadcrumb from '@ui/Breadcrumb.astro'; +import Pagination from '@ui/Pagination.astro'; +import Avatar from '@ui/Avatar.astro'; +import Icon from '@ui/Icon.astro'; +import Dropdown from '@ui/Dropdown.astro'; +import Accordion from '@ui/Accordion.astro'; +import Spinner from '@ui/Spinner.astro'; +import Rating from '@ui/Rating.astro'; +import Steps from '@ui/Steps.astro'; +import StatusDot from '@ui/StatusDot.astro'; +import Toast from '@ui/Toast.astro'; +import InputIcon from '@ui/form/InputIcon.astro'; +import InputGroup from '@ui/InputGroup.astro'; +import Range from '@ui/Range.astro'; +import Tag from '@ui/Tag.astro'; +import Ribbon from '@ui/Ribbon.astro'; +import Flag from '@ui/Flag.astro'; +import Payment from '@ui/Payment.astro'; +import Timeline from '@ui/Timeline.astro'; +import Empty from '@ui/Empty.astro'; +import NavSegmented from '@ui/NavSegmented.astro'; +import ListGroup from '@ui/ListGroup.astro'; +import ListGroupItem from '@ui/ListGroupItem.astro'; const codeExample = `// JavaScript example function greetUser(name) { console.log(\`Hello, \${name}!\`); return true; -}` +}`; --- - -
- -
- - - -
-
-

Heading 1

-

Heading 2

-

Heading 3

-

Heading 4

-
Heading 5
-
Heading 6
-
-
-

This is a lead paragraph with larger text.

-

- This is a regular paragraph with bold text, italic text, and - underlined text. -

-

This is small muted text.

-

Primary text color

-

Success text color

-

Danger text color

-
-
-
-
-
+ +
+ +
+ + + +
+
+

Heading 1

+

Heading 2

+

Heading 3

+

Heading 4

+
Heading 5
+
Heading 6
+
+
+

This is a lead paragraph with larger text.

+

This is a regular paragraph with bold text, italic text, and + underlined text.

+

This is small muted text.

+

Primary text color

+

Success text color

+

Danger text color

+
+
+
+
+
- -
- - - -
-
-

Standard Buttons

- -
-
-

Button Sizes

- -
+
+

Button Sizes

+ +
-
-
-
-
+

Icon Buttons

+ +
+
+ + + - -
- - - -

This is a simple card with header and body content.

-
-
-
+ +
+ + + +

This is a simple card with header and body content.

+
+
+
-
- - - -

This card includes a footer section.

-
- -
- Cancel - Save -
-
-
-
+
+ + + +

This card includes a footer section.

+
+ +
+ Cancel + Save +
+
+
+
- -
- - - - - - - - - -
+ +
+ + + + + + + + + +
- -
- - - -
-
- - - - - - -
-
- - -
-
-
-
-
+ +
+ + + +
+
+ + + + + + +
+
+ + +
+
+
+
+
- -
- - - -
-
- -
-
- -
-
- -
-
- -
-
-
-
-
+ +
+ + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
- -
- - - -
-
- - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
+ +
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
- -
- - - -
-
-

Tabs

-
-
+ +
+ + + +
+
+

Tabs

+
+
-

Pills Navigation

-
-
+

Pills Navigation

+
+
-

Breadcrumb

- -
-
-

Pagination

- { - /* active-item="2" is a STRING in Liquid; `i == "2"` never matches the - integer index, so no page renders active — reproduced by passing a string. */ - } - +

Breadcrumb

+ +
+
+

Pagination

+ {/* activeItem="2" is a string — strict equality never matches integer index, so no page is active */} + -

Pagination with Text

- -
-
-
-
-
+

Pagination with Text

+ +
+
+
+
+
- -
- - - -

Unordered List

-
    -
  • First item
  • -
  • Second item
  • -
  • Third item
  • -
+ +
+ + + +

Unordered List

+
    +
  • First item
  • +
  • Second item
  • +
  • Third item
  • +
-

Ordered List

-
    -
  1. First item
  2. -
  3. Second item
  4. -
  5. Third item
  6. -
-
-
-
+

Ordered List

+
    +
  1. First item
  2. +
  3. Second item
  4. +
  5. Third item
  6. +
+
+
+
- -
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
NameEmailStatus
John Doejohn@example.com
Jane Smithjane@example.com
Bob Johnsonbob@example.com
-
-
-
-
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameEmailStatus
John Doejohn@example.com
Jane Smithjane@example.com
Bob Johnsonbob@example.com
+
+
+
+
- -
- - - -
-
- - - - -
-
- - - -
-
- - - -
-
-
-
-
+ +
+ + + +
+
+ + + + +
+
+ + + +
+
+ + + +
+
+
+
+
- -
- - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-
-
+ +
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+
+
+
- -
- - - - - - - - - -
+ +
+ + + + + + + + + +
- -
- - - -
-
-

Accordion

-
- -
-
-
-

Spinners

-
-
- - - - -
-
+ +
+ + + +
+
+

Accordion

+
+ +
+
+
+

Spinners

+
+
+ + + + +
+
-

Rating

-
- -
+

Rating

+
+ +
-

Steps

-
- -
-
- -
-
-
-
-
-
+

Steps

+
+ +
+
+ +
+
+
+
+
+
- -
- - - -

Status Dots

-
- - - - -
+ +
+ + + +

Status Dots

+
+ + + + +
-

Toast Notifications

-
- -
-
-
-
+

Toast Notifications

+
+ +
+
+
+
- -
- - - -

Input with Icons

-
- -
-
- -
+ +
+ + + +

Input with Icons

+
+ +
+
+ +
-

Input Groups

-
- -
-
- -
+

Input Groups

+
+ +
+
+ +
-

Range Slider

-
- -
-
-
-
+

Range Slider

+
+ +
+
+
+
- -
- - - -

Tags

-
- - - -
+ +
+ + + +

Tags

+
+ + + +
-

Ribbons

-
- -
-
- -
-
-
-
+

Ribbons

+
+ +
+
+ +
+
+
+
- -
- - - -

Flags

-
-
- - - - - - - - - - -
-
+ +
+ + + +

Flags

+
+
+ + + + + + + + + + +
+
-

Payment Icons

-
-
- - - - - - - - - - - - - - - - -
-
-
-
-
+

Payment Icons

+
+
+ + + + + + + + + + + + + + + + +
+
+
+
+
- -
- - - - - - -
+ +
+ + + + + + +
- -
- - - - - - -
+ +
+ + + + + + +
- -
- - - - - - - + +
+ + + + + + + - - + + - - - - -
+ + +
+
+
- -
- - - -

Basic Button Group

- - - - - + +
+ + + +

Basic Button Group

+ + + + + -

Button Toolbar

- +

Button Toolbar

+ -

Vertical Button Group

- - - - - -
-
-
+

Vertical Button Group

+ + + + + +
+
+
- -
- - - -

Basic Segmented

-
- -
+ +
+ + + +

Basic Segmented

+
+ +
-

With Icons

-
- -
+

With Icons

+
+ +
-

With Emojis

-
- -
+

With Emojis

+
+ +
-

With Icons and Text

-
- -
-
-
-
+

With Icons and Text

+
+ +
+
+
+
- -
- - - -
-
-

Basic Collapse

- -
-
This is collapsed content that can be toggled. It's hidden by default and shown when the button is clicked.
-
-
-
-

Multiple Targets

- - - - -
-
First collapsible content.
-
-
-
Second collapsible content.
-
-
-
-
-
-
+ +
+ + + +
+
+

Basic Collapse

+ +
+
+ This is collapsed content that can be toggled. It's hidden by default and shown when the button is clicked. +
+
+
+
+

Multiple Targets

+ + + + +
+
+ First collapsible content. +
+
+
+
+ Second collapsible content. +
+
+
+
+
+
+
- -
- - - -
-
-

Tooltips

- - - - - - -
-
-

Popovers

- - - - -
-
-
-
-
+ +
+ + + +
+
+

Tooltips

+ + + + + + +
+
+

Popovers

+ + + + +
+
+
+
+
- -
- - - - - - - Home - - - - Active item - - - - Settings - - - - Profile - - - - Disabled item - - - - -
+ +
+ + + + + + + Home + + + + Active item + + + + Settings + + + + Profile + + + + Disabled item + + + + +
- -
- - - -

Blockquote

-
-

This is a blockquote example with some sample text to demonstrate the styling.

-
- Someone famous in Source Title -
-
+ +
+ + + +

Blockquote

+
+

This is a blockquote example with some sample text to demonstrate the styling.

+
+ Someone famous in Source Title +
+
-

Code Block

-
+

Code Block

+
-

Inline Elements

-

This paragraph contains inline code, Ctrl + S keyboard shortcut, and highlighted text.

-
-
-
-
+

Inline Elements

+

This paragraph contains inline code, Ctrl + S keyboard shortcut, and highlighted text.

+ + + +
diff --git a/preview/pages/auth-lock.astro b/preview/pages/auth-lock.astro index 0228efddd..c7b0d71db 100644 --- a/preview/pages/auth-lock.astro +++ b/preview/pages/auth-lock.astro @@ -1,9 +1,9 @@ --- -// title really is "Forgot password" in the Liquid source) -import SingleLayout from '@shared/layouts/SingleLayout.astro' -import AuthLockCard from '@shared/components/cards/AuthLockCard.astro' +// Title is "Forgot password" (matches auth-lock card, not page name). +import SingleLayout from '@shared/layouts/SingleLayout.astro'; +import AuthLockCard from '@shared/components/cards/AuthLockCard.astro'; --- - + diff --git a/preview/pages/avatars.astro b/preview/pages/avatars.astro index 75ceb46b8..345e2c6e1 100644 --- a/preview/pages/avatars.astro +++ b/preview/pages/avatars.astro @@ -1,169 +1,166 @@ --- -// Note: the `description` front matter key is inert in the dev build (no -// in the reference output), so it is not ported. -import DefaultLayout from '@shared/layouts/DefaultLayout.astro' -import Avatar from '@ui/Avatar.astro' -import AvatarUpload from '@ui/AvatarUpload.astro' -import AvatarList from '@ui/AvatarList.astro' -import Card from '@ui/Card.astro' -import CardBody from '@ui/CardBody.astro' -import CardTitle from '@ui/CardTitle.astro' -import people from '@data/people.json' -import { site } from '@shared/lib/site' -import { firstLetters } from '@shared/lib/string-format' +import DefaultLayout from '@shared/layouts/DefaultLayout.astro'; +import Avatar from '@ui/Avatar.astro'; +import AvatarUpload from '@ui/AvatarUpload.astro'; +import AvatarList from '@ui/AvatarList.astro'; +import Card from '@ui/Card.astro'; +import CardBody from '@ui/CardBody.astro'; +import CardTitle from '@ui/CardTitle.astro'; +import people from '@data/people.json'; +import { site } from '@shared/lib/site'; +import { firstLetters } from '@shared/lib/string-format'; -const iconIcons = ['user', 'settings', 'car', 'balloon', 'users', 'users-group', 'apps', 'ghost'] +const iconIcons = ['user', 'settings', 'car', 'balloon', 'users', 'users-group', 'apps', 'ghost']; -// Liquid: {% for color in site.colors %} — the iterated keys match site.themeColors -// (blue..cyan), confirmed against the reference output. -const colors = site.themeColors +// themeColors keys (blue..cyan). +const colors = site.themeColors; -const people8 = people.slice(0, 8) -const people5 = people.slice(0, 5) -const sizes = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl'] -const listSizes = ['xxs', 'xs', 'sm', 'md', 'lg'] -const uploadSizes = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl'] -const statusColors = ['red', 'green', 'blue', 'yellow', 'secondary'] -const brands = ['netflix', 'amazon', 'messenger', 'figma', 'twitch'] +const people8 = people.slice(0, 8); +const people5 = people.slice(0, 5); +const sizes = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl']; +const listSizes = ['xxs', 'xs', 'sm', 'md', 'lg']; +const uploadSizes = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl']; +const statusColors = ['red', 'green', 'blue', 'yellow', 'secondary']; +const brands = ['netflix', 'amazon', 'messenger', 'figma', 'twitch']; --- -
-
- - - Default avatar - - - -
-
- - - Avatar with icon +
+
+ + + Default avatar + + + +
+
+ + + Avatar with icon -
- {iconIcons.map((icon) => )} -
-
-
-
-
- - - Avatar with icon +
+ {iconIcons.map((icon) => )} +
+
+
+
+
+ + + Avatar with icon -
- {colors.map((color) => )} -
-
-
-
-
- - - Simple avatar -
- {people8.map((person) => )} -
-
-
-
-
- - - Avatar placeholder -
- {people8.map((person) => )} -
-
-
-
-
- - - Avatar shapes -
- - - -
-
-
-
-
- - - Avatar sizes -
-
-
- {sizes.map((size) => )} -
-
-
-
- {sizes.map((size) => )} -
-
-
-
-
-
-
- - - Avatar lists -
- { - listSizes.map((size) => ( - <> -
- - {people5.map((person) => ( - - ))} - - -
-
- - {people5.map((person) => ( - - ))} - - -
- - )) - } -
-
-
-
-
- - - Avatar placeholder - {uploadSizes.map((size) => )} - - -
-
- - - Avatar statuses - {statusColors.map((color, i) => )} - - -
-
- - - Avatar brands - {brands.map((brand, i) => )} - - -
-
+
+ {colors.map((color) => )} +
+
+
+
+
+ + + Simple avatar +
+ {people8.map((person) => )} +
+
+
+
+
+ + + Avatar placeholder +
+ {people8.map((person) => )} +
+
+
+
+
+ + + Avatar shapes +
+ + + +
+
+
+
+
+ + + Avatar sizes +
+
+
+ {sizes.map((size) => )} +
+
+
+
+ {sizes.map((size) => )} +
+
+
+
+
+
+
+ + + Avatar lists +
+ { + listSizes.map((size) => ( + <> +
+ + {people5.map((person) => ( + + ))} + + +
+
+ + {people5.map((person) => ( + + ))} + + +
+ + )) + } +
+
+
+
+
+ + + Avatar placeholder + {uploadSizes.map((size) => )} + + +
+
+ + + Avatar statuses + {statusColors.map((color, i) => )} + + +
+
+ + + Avatar brands + {brands.map((brand, i) => )} + + +
+
diff --git a/preview/pages/badges.astro b/preview/pages/badges.astro index 639d44e84..7f41d180b 100644 --- a/preview/pages/badges.astro +++ b/preview/pages/badges.astro @@ -1,205 +1,206 @@ --- -// Liquid: colors = ['default'] + site.colors keys + ['dark', 'light'] -import BadgesList from '@ui/BadgesList.astro' -import DefaultLayout from '@shared/layouts/DefaultLayout.astro' -import Icon from '@ui/Icon.astro' -import ButtonList from '@ui/ButtonList.astro' -import Card from '@ui/Card.astro' -import CardBody from '@ui/CardBody.astro' -import CardTitle from '@ui/CardTitle.astro' -import DropdownMenu from '@ui/DropdownMenu.astro' -import site from '@data/site.json' -import { ucFirst } from '@shared/lib/string-format' +// colors = ['default'] + site.colors keys + ['dark', 'light'] -const colors = ['default', ...Object.keys(site.colors), 'dark', 'light'] -const sizes = ['sm', 'md', 'lg'] +import BadgesList from '@ui/BadgesList.astro'; +import DefaultLayout from '@shared/layouts/DefaultLayout.astro'; +import Icon from '@ui/Icon.astro'; +import ButtonList from '@ui/ButtonList.astro'; +import Card from '@ui/Card.astro'; +import CardBody from '@ui/CardBody.astro'; +import CardTitle from '@ui/CardTitle.astro'; +import DropdownMenu from '@ui/DropdownMenu.astro'; +import site from '@data/site.json'; +import { ucFirst } from '@shared/lib/string-format'; + +const colors = ['default', ...Object.keys(site.colors), 'dark', 'light']; +const sizes = ['sm', 'md', 'lg']; --- -
-
-
-
- - -

Example heading New

-

Example heading New

-

Example heading New

-

Example heading New

-
Example heading New
-
Example heading New
-
-
-
-
- - - Badge sizes +
+
+
+
+ + +

Example heading New

+

Example heading New

+

Example heading New

+

Example heading New

+
Example heading New
+
Example heading New
+
+
+
+
+ + + Badge sizes -
- { - sizes.map((size) => ( - - Default - - Left icon - - - Right icon - - - - - - - )) - } -
-
-
-
-
- - - Positioned badges +
+ { + sizes.map((size) => ( + + Default + + Left icon + + + Right icon + + + + + + )) + } +
+
+
+
+
+ + + Positioned badges - - + + - + - + - + - + - - - - -
-
-
-
-
-
- - - Basic badges - - {colors.map((color) => {ucFirst(color)})} - - - -
-
- - - Light badges - - {colors.map((color) => {ucFirst(color)})} - - - -
-
- - - Outline badges - - {colors.map((color) => {ucFirst(color)})} - - - -
-
- - - Badges with icons - - { - colors.map((color) => ( - - {' '} - {ucFirst(color)}{' '} - - )) - } - - - -
-
-
-
-
-
-
- - - - { - colors.map((color, index) => ( - - )) - } - - - -
-
- - - - { - colors.map((color, index) => ( - - )) - } - - - -
-
- - - - { - colors.map((color) => ( - - )) - } - - - -
-
-
-
+ + +
+
+
+
+
+
+
+
+ + + Basic badges + + {colors.map((color) => {ucFirst(color)})} + + + +
+
+ + + Light badges + + {colors.map((color) => {ucFirst(color)})} + + + +
+
+ + + Outline badges + + {colors.map((color) => {ucFirst(color)})} + + + +
+
+ + + Badges with icons + + { + colors.map((color) => ( + + {' '} + {ucFirst(color)}{' '} + + )) + } + + + +
+
+
+
+
+
+
+ + + + { + colors.map((color, index) => ( + + )) + } + + + +
+
+ + + + { + colors.map((color, index) => ( + + )) + } + + + +
+
+ + + + { + colors.map((color) => ( + + )) + } + + + +
+
+
+
diff --git a/preview/pages/blank.astro b/preview/pages/blank.astro index 4fd2dae0d..0324fb35b 100644 --- a/preview/pages/blank.astro +++ b/preview/pages/blank.astro @@ -1,10 +1,9 @@ --- -// front matter is inert in the Liquid templates (never referenced) — the -// empty page header comes from the missing `page-header` key. -import DefaultLayout from '@shared/layouts/DefaultLayout.astro' -import Empty from '@ui/Empty.astro' +// front matter keys unused — empty page header comes from missing `page-header` key. +import DefaultLayout from '@shared/layouts/DefaultLayout.astro'; +import Empty from '@ui/Empty.astro'; --- - + diff --git a/preview/pages/buttons.astro b/preview/pages/buttons.astro index f293f8014..530db5243 100644 --- a/preview/pages/buttons.astro +++ b/preview/pages/buttons.astro @@ -1,201 +1,191 @@ --- -import DefaultLayout from '@shared/layouts/DefaultLayout.astro' -import Icon from '@ui/Icon.astro' -import Button from '@ui/Button.astro' -import ButtonList from '@ui/ButtonList.astro' -import Card from '@ui/Card.astro' -import CardHeader from '@ui/CardHeader.astro' -import CardBody from '@ui/CardBody.astro' -import site from '@data/site.json' +import DefaultLayout from '@shared/layouts/DefaultLayout.astro'; +import Icon from '@ui/Icon.astro'; +import Button from '@ui/Button.astro'; +import ButtonList from '@ui/ButtonList.astro'; +import Card from '@ui/Card.astro'; +import CardHeader from '@ui/CardHeader.astro'; +import CardBody from '@ui/CardBody.astro'; +import site from '@data/site.json'; -type ColorEntry = [string, { title: string; icon?: string }] +type ColorEntry = [string, { title: string; icon?: string }]; -// Liquid iterates the site.json objects as [key, value] pairs. -// themeColors/colors entries have no icon — ui/icon.html renders nothing there. -const themeColors = Object.entries(site.themeColors) as ColorEntry[] -const colors = Object.entries(site.colors) as ColorEntry[] -const socialColors = Object.entries(site.socialColors) as ColorEntry[] +// site.json objects iterated as [key, value] pairs. +// themeColors/colors entries have no icon — Icon renders nothing there. +const themeColors = Object.entries(site.themeColors) as ColorEntry[]; +const colors = Object.entries(site.colors) as ColorEntry[]; +const socialColors = Object.entries(site.socialColors) as ColorEntry[]; -const actions = ['edit', 'copy', 'settings', 'clipboard', 'x'] -const sizes = ['sm', 'md', 'lg', 'xl'] +const actions = ['edit', 'copy', 'settings', 'clipboard', 'x']; +const sizes = ['sm', 'md', 'lg', 'xl']; --- -
-
- - - - - { - themeColors.map(([name, color]) => ( - - {color.icon && } {color.title} - - )) - } - - - -
-
- - - - - { - themeColors.map(([name, color]) => ( - - {color.icon && } {color.title} - - )) - } - - - -
-
- - - - - { - themeColors.map(([name, color]) => ( - - {color.icon && } {color.title} - - )) - } - - - -
-
- - - - - { - themeColors.map(([name, color]) => ( - - {color.icon && } {color.title} - - )) - } - - - -
-
- - - - - { - themeColors.map(([name, color]) => ( - - {color.icon && } {color.title} - - )) - } - - - -
-
- - - - - { - colors.map(([name, color]) => ( - - {color.icon && } {color.title} - - )) - } - - - -
-
- - - - - {socialColors.map(([name, app]) => {app.icon && })} - - - -
-
- - - - - { - socialColors.map(([name, app]) => ( - - {app.icon && } {app.title} - - )) - } - - - -
-
- - - -
- { - actions.map((action) => ( - - - - )) - } -
-
-
-
- -
- - - -
- { - sizes.map((size) => ( - -
-
-
-
-
+
+
+ + + + + { + themeColors.map(([name, color]) => ( + {color.icon && } {color.title} + )) + } + + + +
+
+ + + + + { + themeColors.map(([name, color]) => ( + {color.icon && } {color.title} + )) + } + + + +
+
+ + + + + { + themeColors.map(([name, color]) => ( + {color.icon && } {color.title} + )) + } + + + +
+
+ + + + + { + themeColors.map(([name, color]) => ( + {color.icon && } {color.title} + )) + } + + + +
+
+ + + + + { + themeColors.map(([name, color]) => ( + {color.icon && } {color.title} + )) + } + + + +
+
+ + + + + { + colors.map(([name, color]) => ( + {color.icon && } {color.title} + )) + } + + + +
+
+ + + + + { + socialColors.map(([name, app]) => ( + {app.icon && } + )) + } + + + +
+
+ + + + + { + socialColors.map(([name, app]) => ( + {app.icon && } {app.title} + )) + } + + + +
+
+ + + +
+ { + actions.map((action) => ( + + + + )) + } +
+
+
+
+ +
+ + + +
+ { + sizes.map((size) => ( + +
+
+
+
+
diff --git a/preview/pages/changelog.astro b/preview/pages/changelog.astro index c87bf938f..ab2d22452 100644 --- a/preview/pages/changelog.astro +++ b/preview/pages/changelog.astro @@ -1,18 +1,11 @@ --- -import ProseLayout from '@shared/layouts/ProseLayout.astro' -import { renderMarkdown } from '@shared/lib/render-markdown' -// Liquid: {{ changelog | renderContent: "md" }} — `changelog` is the raw -// content of core/CHANGELOG.md (shared/e11ty/data.mjs), rendered by -// Eleventy's markdown-it. renderMarkdown() mirrors that engine exactly -// (markdown-it 14.3.0, { html: true }, indented code blocks disabled) — -// output verified byte-identical to the reference build. Injected via -// set:html: the fragment contains entities and a literal `{$prefix}` token -// that must not be re-escaped or parsed as JSX. -import changelog from '../../core/CHANGELOG.md?raw' +import ProseLayout from '@shared/layouts/ProseLayout.astro'; +import { renderMarkdown } from '@shared/lib/render-markdown'; +import changelog from '../../core/CHANGELOG.md?raw'; -const changelogHtml = renderMarkdown(changelog) +const changelogHtml = renderMarkdown(changelog); --- - + diff --git a/preview/pages/colorpicker.astro b/preview/pages/colorpicker.astro index 00482cb2c..dbc9e0e17 100644 --- a/preview/pages/colorpicker.astro +++ b/preview/pages/colorpicker.astro @@ -1,31 +1,34 @@ --- -// Liquid: {% for color in site.colors %} — forloop.index (1-based) → id, -// color[1].hex → value. -import DefaultLayout from '@shared/layouts/DefaultLayout.astro' -import Card from '@ui/Card.astro' -import CardBody from '@ui/CardBody.astro' -import CardTitle from '@ui/CardTitle.astro' -import Colorpicker from '@ui/Colorpicker.astro' -import site from '@data/site.json' +import DefaultLayout from '@shared/layouts/DefaultLayout.astro'; +import Card from '@ui/Card.astro'; +import CardBody from '@ui/CardBody.astro'; +import CardTitle from '@ui/CardTitle.astro'; +import Colorpicker from '@ui/Colorpicker.astro'; +import site from '@data/site.json'; -const colors = Object.values(site.colors) as { hex: string }[] +const colors = Object.values(site.colors) as { hex: string }[]; --- - - - - Basic -
- { - colors.map((color, index) => ( -
-
- -
-
- )) - } -
-
-
+ + + + Basic +
+ { + colors.map((color, index) => ( +
+
+ +
+
+ )) + } +
+
+
diff --git a/preview/pages/colors.astro b/preview/pages/colors.astro index 9cfe865af..5ee77374d 100644 --- a/preview/pages/colors.astro +++ b/preview/pages/colors.astro @@ -1,235 +1,247 @@ --- -import DefaultLayout from '@shared/layouts/DefaultLayout.astro' -import FormGroup from '@ui/FormGroup.astro' -import Card from '@ui/Card.astro' -import CardBody from '@ui/CardBody.astro' -import Avatar from '@ui/Avatar.astro' -import CardTitle from '@ui/CardTitle.astro' -import site from '@data/site.json' +import DefaultLayout from '@shared/layouts/DefaultLayout.astro'; +import CaptureScript from '@shared/components/CaptureScript.astro'; +import FormGroup from '@ui/FormGroup.astro'; +import Card from '@ui/Card.astro'; +import CardBody from '@ui/CardBody.astro'; +import Avatar from '@ui/Avatar.astro'; +import CardTitle from '@ui/CardTitle.astro'; +import site from '@data/site.json'; -type ColorEntry = [string, { title: string; hex: string; abbr?: string; icon?: string }] +type ColorEntry = [string, { title: string; hex: string; abbr?: string; icon?: string }]; -// Liquid iterates the site.json objects as [key, value] pairs. -const colors = Object.entries(site.colors) as ColorEntry[] -const lightColors = Object.entries(site.lightColors) as ColorEntry[] -const grayColors = Object.entries(site.grayColors) as ColorEntry[] -const socialColors = Object.entries(site.socialColors) as ColorEntry[] +const colors = Object.entries(site.colors) as ColorEntry[]; +const lightColors = Object.entries(site.lightColors) as ColorEntry[]; +const grayColors = Object.entries(site.grayColors) as ColorEntry[]; +const socialColors = Object.entries(site.socialColors) as ColorEntry[]; -// Liquid: colors keys + inverted, white, transparent (pushed before the gradient loops). -const gradientColors = [...Object.keys(site.colors), 'inverted', 'white', 'transparent'] +const gradientColors = [...Object.keys(site.colors), 'inverted', 'white', 'transparent']; --- -
-
- - -
- { - colors.map(([name, color]) => ( -
-
-
- -
-
- {color.title} -
- {color.hex} -
-
-
- )) - } -
-
-
-
-
- - -
- { - lightColors.map(([name, color]) => ( -
-
-
- -
-
- {color.title} -
- {color.hex} -
-
-
- )) - } -
-
-
-
-
- - -
- { - grayColors.map(([name, color]) => ( -
-
-
- -
-
- {color.title} -
- {color.hex} -
-
-
- )) - } -
-
-
-
-
- - -
- { - socialColors.map(([name, color]) => ( -
-
-
- -
-
- {color.title} -
- {color.hex} -
-
-
- )) - } -
-
-
-
-
-
-
- - - Gradient -
-
-
- - - - - - -
-
- - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
- - -
-
-
- {gradientColors.map((color) =>
)} -
-
-
-
- {gradientColors.map((color) =>
)} -
-
-
- - -
-
-
-
+
+
+ + +
+ { + colors.map(([name, color]) => ( +
+
+
+ +
+
+ {color.title} +
+ {color.hex} +
+
+
+ )) + } +
+
+
+
+
+ + +
+ { + lightColors.map(([name, color]) => ( +
+
+
+ +
+
+ {color.title} +
+ {color.hex} +
+
+
+ )) + } +
+
+
+
+
+ + +
+ { + grayColors.map(([name, color]) => ( +
+
+
+ +
+
+ {color.title} +
+ {color.hex} +
+
+
+ )) + } +
+
+
+
+
+ + +
+ { + socialColors.map(([name, color]) => ( +
+
+
+ +
+
+ {color.title} +
+ {color.hex} +
+
+
+ )) + } +
+
+
+
+
+
+
+ + + Gradient +
+
+
+ + + + + + +
+
+ + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+ { + gradientColors.map((color) => ( +
+ )) + } +
+
+
+
+ { + gradientColors.map((color) => ( +
+ )) + } +
+
+
+ + +
+
+
+
- - - + updateGradient(); + }); + + + diff --git a/preview/pages/dashboard-crypto.astro b/preview/pages/dashboard-crypto.astro index 2fef984c0..25ea481c7 100644 --- a/preview/pages/dashboard-crypto.astro +++ b/preview/pages/dashboard-crypto.astro @@ -1,332 +1,337 @@ --- -import CardActions from '@ui/CardActions.astro' -import DefaultLayout from '@shared/layouts/DefaultLayout.astro' -import Avatar from '@ui/Avatar.astro' -import Trending from '@ui/Trending.astro' -import CardDropdown from '@ui/CardDropdown.astro' -import Card from '@ui/Card.astro' -import CardHeader from '@ui/CardHeader.astro' -import CardBody from '@ui/CardBody.astro' -import NavSegmented from '@ui/NavSegmented.astro' -import Chart from '@ui/Chart.astro' -import SwitchIcon from '@ui/SwitchIcon.astro' -import cryptoCurrencies from '@data/crypto-currencies.json' -import cryptoMarkets from '@data/crypto-markets.json' -import cryptoOrders from '@data/crypto-orders.json' -import { parseCurrency, roundTo } from '@shared/lib/string-format' +import CardActions from '@ui/CardActions.astro'; +import DefaultLayout from '@shared/layouts/DefaultLayout.astro'; +import Avatar from '@ui/Avatar.astro'; +import Trending from '@ui/Trending.astro'; +import CardDropdown from '@ui/CardDropdown.astro'; +import Card from '@ui/Card.astro'; +import CardHeader from '@ui/CardHeader.astro'; +import CardBody from '@ui/CardBody.astro'; +import NavSegmented from '@ui/NavSegmented.astro'; +import Chart from '@ui/Chart.astro'; +import SwitchIcon from '@ui/SwitchIcon.astro'; +import cryptoCurrencies from '@data/crypto-currencies.json'; +import cryptoMarkets from '@data/crypto-markets.json'; +import cryptoOrders from '@data/crypto-orders.json'; +import { parseCurrency, roundTo } from '@shared/lib/string-format'; interface Currency { - 'symbol': string - 'price': string - 'p24h': number - 'volume-24h': string + symbol: string; + price: string; + p24h: number; + 'volume-24h': string; } -const currencies = cryptoCurrencies as Currency[] -const find = (symbol: string) => currencies.find((c) => c.symbol === symbol)! -const btc = find('BTC') -const ltc = find('LTC') -const eth = find('ETH') -const xmr = find('XMR') +const currencies = cryptoCurrencies as Currency[]; +const find = (symbol: string) => currencies.find((c) => c.symbol === symbol)!; +const btc = find('BTC'); +const ltc = find('LTC'); +const eth = find('ETH'); +const xmr = find('XMR'); -const btcBalance = 2.3 -const btcPriceNum = parseCurrency(btc.price) -const totalUsd = Math.round(btcPriceNum * btcBalance).toLocaleString('en-US') -// Liquid `divided_by` then `round: 8` (trailing zeros dropped by number output) -const ltcBtc = roundTo(parseCurrency(ltc.price) / btcPriceNum) -const ethBtc = roundTo(parseCurrency(eth.price) / btcPriceNum) -const xmrBtc = roundTo(parseCurrency(xmr.price) / btcPriceNum) +const btcBalance = 2.3; +const btcPriceNum = parseCurrency(btc.price); +const totalUsd = Math.round(btcPriceNum * btcBalance).toLocaleString('en-US'); +// divided_by then round to 8 decimals (trailing zeros dropped in output) +const ltcBtc = roundTo(parseCurrency(ltc.price) / btcPriceNum); +const ethBtc = roundTo(parseCurrency(eth.price) / btcPriceNum); +const xmrBtc = roundTo(parseCurrency(xmr.price) / btcPriceNum); -const markets = (cryptoMarkets as { coin: string; price: string; volume: string; change: string }[]).slice(0, 10) -const orders = cryptoOrders as { sell_orders: { price: string; btc: string; sum: string }[]; buy_orders: { price: string; btc: string; sum: string }[] } -const operationCurrencies = currencies.slice(0, 20) +const markets = (cryptoMarkets as { coin: string; price: string; volume: string; change: string }[]).slice(0, 10); +const orders = cryptoOrders as { sell_orders: { price: string; btc: string; sum: string }[]; buy_orders: { price: string; btc: string; sum: string }[] }; +const operationCurrencies = currencies.slice(0, 20); --- - -
-
-
-
- - -
-
-
Total balance
-
-
- -
-
-
- ${totalUsd} - {btcBalance} {btc.symbol} -
-
- - Since last week -
-
-
-
+ +
+
+
+
+ + +
+
+
Total balance
+
+
+ +
+
+
+ ${totalUsd} + {btcBalance} {btc.symbol} +
+
+ + Since last week +
+
+
+
-
- - -
-
-
USD/BTC
-
-
- -
-
-
- {btc.price} - {btc.price} -
-
- Volume: {btc['volume-24h']} -
-
-
-
+
+ + +
+
+
USD/BTC
+
+
+ +
+
+
+ {btc.price} + {btc.price} +
+
+ Volume: {btc['volume-24h']} +
+
+
+
-
- - -
-
-
LTC/BTC
-
-
- -
-
-
- {ltcBtc} - {ltc.price} -
-
- Volume: {ltc['volume-24h'].replace('$', '')} -
-
-
-
+
+ + +
+
+
LTC/BTC
+
+
+ +
+
+
+ {ltcBtc} + {ltc.price} +
+
+ Volume: {ltc['volume-24h'].replace('$', '')} +
+
+
+
-
- - -
-
-
ETH/BTC
-
-
- -
-
-
- {ethBtc} - {eth.price} -
-
- Volume: {eth['volume-24h'].replace('$', '')} -
-
-
-
+
+ + +
+
+
ETH/BTC
+
+
+ +
+
+
+ {ethBtc} + {eth.price} +
+
+ Volume: {eth['volume-24h'].replace('$', '')} +
+
+
+
-
- - -
-
-
XMR/BTC
-
-
- -
-
-
- {xmrBtc} - {xmr.price} -
-
- Volume: {xmr['volume-24h'].replace('$', '')} -
-
-
-
-
-
-
-
-
- - -
Markets
- - - -
- - - - - - - - - - - - { - markets.map((market) => ( - - - - - - - - )) - } - -
- - CoinPriceVolumeChange
- - {market.coin}{market.price}{market.volume} - -
-
-
+
+ + +
+
+
XMR/BTC
+
+
+ +
+
+
+ {xmrBtc} + {xmr.price} +
+
+ Volume: {xmr['volume-24h'].replace('$', '')} +
+
+
+
+
+
+
+
+
+ + +
Markets
+ + + +
+ + + + + + + + + + + + { + markets.map((market) => ( + + + + + + + + )) + } + +
+ + CoinPriceVolumeChange
+ + {market.coin}{market.price}{market.volume} + +
+
+
-
- - -
LTC/BTC
- - - -
- - - -
-
-
-
-
-
-
- - -
Operations
- - - -
+
+ + +
LTC/BTC
+ + + +
+ + + +
+
+
+
+
+
+
+ + +
Operations
+ + + +
- -

Place new order:

+ +

Place new order:

-
- - - -
+
+ + + +
-
- - - -
+
+ + + +
-
- - - -
+
+ + + +
-
- -
+
+ +
-

The final amount could change depending on current market conditions.

-
-
-
+

The final amount could change depending on current market conditions.

+ + +
-
- - -
Sell Orders
- - - -
- - - - - - - - - - { - orders.sell_orders.map((order) => ( - - - - - - )) - } - -
PriceBTCSum(BTC)
{order.price}{order.btc}{order.sum}
-
-
+
+ + +
Sell Orders
+ + + +
+ + + + + + + + + + { + orders.sell_orders.map((order) => ( + + + + + + )) + } + +
PriceBTCSum(BTC)
{order.price}{order.btc}{order.sum}
+
+
-
- - -
Buy Orders
- - - -
- - - - - - - - - - { - orders.buy_orders.map((order) => ( - - - - - - )) - } - -
PriceBTCSum(BTC)
{order.price}{order.btc}{order.sum}
-
-
-
-
-
+
+ + +
Buy Orders
+ + + +
+ + + + + + + + + + { + orders.buy_orders.map((order) => ( + + + + + + )) + } + +
PriceBTCSum(BTC)
{order.price}{order.btc}{order.sum}
+
+
+
+
+
diff --git a/preview/pages/email-inbox.astro b/preview/pages/email-inbox.astro index b6bdb38f0..59b02a1ec 100644 --- a/preview/pages/email-inbox.astro +++ b/preview/pages/email-inbox.astro @@ -1,212 +1,208 @@ --- -import Subheader from '@ui/Subheader.astro' -import ButtonGroup from '@ui/ButtonGroup.astro' -import DefaultLayout from '@shared/layouts/DefaultLayout.astro' -import Button from '@ui/Button.astro' -import Icon from '@ui/Icon.astro' -import Card from '@ui/Card.astro' -import Offcanvas from '@ui/Offcanvas.astro' -import Progress from '@ui/Progress.astro' -import Modal from '@shared/components/modals/Modal.astro' -import NewEmailModalContent from '@shared/components/modals/NewEmailModalContent.astro' -import mails from '@data/mails.json' ---- - - -
- +
+
+ + + + + + + diff --git a/preview/pages/emails.astro b/preview/pages/emails.astro index 0d2b176b3..d71f66e10 100644 --- a/preview/pages/emails.astro +++ b/preview/pages/emails.astro @@ -1,95 +1,107 @@ --- // Masonry auto-inits from the data-masonry attribute (page-lib, no init script, // like cards-masonry.astro); fslightbox is a pure page-lib (no init script). -import DefaultLayout from '@shared/layouts/DefaultLayout.astro' -import CardStamp from '@ui/CardStamp.astro' -import Prose from '@ui/Prose.astro' -import { site } from '@shared/lib/site' -import emails from '@data/emails.json' +import DefaultLayout from '@shared/layouts/DefaultLayout.astro'; +import CardStamp from '@ui/CardStamp.astro'; +import Prose from '@ui/Prose.astro'; +import CaptureScript from '@shared/components/CaptureScript.astro'; +import { site } from '@shared/lib/site'; +import emails from '@data/emails.json'; -const emailEntries = Object.entries(emails) +const emailEntries = Object.entries(emails); --- - -
-
-
- -
-
-
-

Tabler Emails

- - {emailEntries.length} eye-catching, customizable and responsive email templates to improve your email communication. No coding skills needed. - - -
-
-
-
-
-
-
- { - emailEntries.map(([key, email]) => ( -
- - {email.descriptionShort} - -
- )) - } -
-
-
+ +
+
+
+ +
+
+
+

Tabler Emails

+ + {emailEntries.length} eye-catching, customizable and responsive email templates to improve your email communication. No coding skills needed. + + +
+
+
+
+
+
+
+ {emailEntries.map(([key, email]) => ( +
+ + {email.descriptionShort} + +
+ ))} +
+
+
-