Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions docs/ISLAND_PARITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Island → Nextra parity matrix

This matrix is the maintained contract for reproducing the established Island documentation in Nextra. The preserved Island source/build at git revision `86f2008^` and `https://echoui.dev/en/` are the presentation and content references. The current package source and declarations remain authoritative for Echo UI 1.1.x API facts.

## Shell, navigation, and content

| Surface | Island contract | Nextra implementation | Deterministic coverage |
| --- | --- | --- | --- |
| Root route | `/` enters the default English documentation without an editorial picker. | The static root permanently redirects to `/en/`. | Route/content inventory and static-export smoke test. |
| Desktop shell | Fixed 60 px header, 272 px sidebar, 704 px article, right-hand outline. | Island theme tokens and geometry are retained at 1440 × 900. | `island-v1.json` desktop light/dark profiles. |
| Mobile shell | 56 px header, a separate 48 px section-menu row, off-canvas navigation, single-column content. | `IslandMobileMenu` controls the Nextra mobile navigation with expanded state and focus-visible styling. | `island-v1.json` mobile light/dark profiles at 390 × 844. |
| Locales and themes | English and Chinese share the route hierarchy; light and dark retain the same geometry. | Every representative baseline route is exercised in `en`/`zh`, light/dark, desktop/mobile. | 16 locale/profile/category combinations per category in `verify-docs-ui.mjs`. |
| Header links | Links contains GitHub, Discord, and Twitter. | The exact three Island destinations are restored. | Content inventory plus interactive UI verification. |
| Guide sidebar | A `Guide` section contains Introduction, Installation, and Declaration; `About` is separated. | Locale-specific Nextra metadata uses Guide/About separators. | Content inventory and rendered sidebar assertions. |
| Page affordances | Edit link, last-updated metadata, previous/next navigation, and footer are visible. | Nextra edit/navigation defaults are restored; the footer contains the MIT license and copyright. | Static output and UI assertions. |
| External prose links | Brand-colored Island link treatment without an appended arrow glyph. | Nextra's external-link icon is hidden in article prose while external semantics remain intact. | Visual baseline assertion. |
| Callouts | Neutral bordered panel without blue info chrome. | Nextra callouts use the Island background, border, color, spacing, and no decorative icon. | Guide baseline assertion. |
| Introduction | Five compact feature bullets under one Features heading. | Updated factual copy retains the five-item list rhythm and avoids five H3 sections. | Bilingual content inventory. |
| Installation | A four-step installation story. | Requirements/compatibility, package installation, Tailwind configuration, then import/render form the four primary steps. | Bilingual content inventory; Tailwind compatibility verifier. |
| About | Project contacts plus Discord and WeChat community choices. | The shipped `/wechat.jpg` QR image and WeChat option are restored in both locales. | Bilingual content inventory and asset smoke test. |
| Declaration | Copy promises the edit affordance that the shell actually provides. | The page-level edit link is enabled and the bilingual declaration copy agrees. | Bilingual content inventory and rendered edit-link check. |

## Component examples

Every entry below is a live rendered preview with an adjacent Preview/Code tab and copy action. There are no intentionally omitted Island variants.

| Component | Required live variants | Count |
| --- | --- | --: |
| Button | Default; Toggle State; Disabled State; Size; Rounded Corners; Button Group | 6 |
| Checkbox | Default; Disabled State; Size; Color; Checkbox Group | 5 |
| Envelope | ADSR Envelope; AHDSR Envelope; Delay/DADSR | 3 |
| Input | Default; Disabled State; Bilateral Mode; Text Mode; Size; Rounded Corners; Progress Bar Color; Minimum and Maximum Values; Step and Sensitivity | 9 |
| Knob | Default; Disabled; Bilateral Rotation Mode; Rotation Angle Range; Labels; Step and Sensitivity; Size-Related; Color-Related; Knob Group | 9 |
| Radio | Default; Disabled State; Size; Color; Radio Group | 5 |
| Slider | Default; Disabled State; Vertical Mode; Bilateral Mode; Adding Coordinates; Step; Custom Styling; Uncontrolled Mode | 8 |
| Switch | Default; Toggled State; Disabled State; Size; Custom Styling | 5 |
| LFO | Default; Delay | 2 |
| Light | Default; On State; Light Color; Size | 4 |
| Oscilloscope | Default | 1 |
| Spectrogram | Audio Data; Axis; Grid; Use Case: EQ3 | 4 |
| VU Meter | Default; Horizontal Mode; Stereo Mode; Compact Mode; Number of Volume Bars; Custom Colors | 6 |
| Waveform | Default | 1 |
| Card | Default; Active State; Real-World Scenario | 3 |

The 71-variant inventory lives in one bilingual data model so labels, source, counts, and rendered examples cannot silently drift between locales. Preview/Code uses an ARIA tablist, supports arrow, Home, and End keys, keeps the active tab in the tab order, and exposes copy success/failure through a polite status. Reduced motion removes transitions. The richer Nextra real-audio/status demonstrations remain above the variant matrix and supplement it.

## API, public types, and data attributes

| Surface | Parity contract | Maintained behavior |
| --- | --- | --- |
| API hierarchy | Component/compound-component headings precede visible tables. | Generated tables remain because they cover the current public declarations. |
| Column order | Attribute / Description / Type / Default. | Both locales render that Island order; translated labels preserve the same order. |
| Public types | Inherited React attributes, compound APIs, callback payloads, exported data shapes, and limitations remain discoverable. | Generated rows plus the inherited-contract paragraph are sourced from the current package types. |
| Controller data attributes | Button, Checkbox, Input, Knob, Radio, Slider, and Switch attributes are listed. | Values and meanings are rendered beside each API. |
| Display data tags | VU Meter segment `data-active` bands and Card `data-toggled` are listed. | Values are based on the current runtime. |

The current Button runtime emits `data-disable`, not the legacy page's `data-disabled`. The docs show the actual runtime spelling because package behavior is the frozen source of truth; changing the shipped runtime solely for documentation parity is out of scope.

## Intentional modernized exceptions

- Nextra and static export remain the documentation toolchain. IslandJS, NextUI, `react-live`, and stale implementation dependencies are not restored.
- Echo UI 1.1.x compatibility remains accurate: React 18.2–19, Tailwind CSS 3/4, tailwind-variants 3.2.x, tailwind-merge 3.6.x, and Tone.js 15.1.22.
- Accessibility guidance, native fallback controls, focus-visible treatment, keyboard-operable source disclosure, reduced-motion behavior, lifecycle cleanup, and error recovery remain.
- Real Web Audio demonstrations, status text, reconnect flows, and restart-safe audio graphs remain and supplement the Island variant examples.
- Generated API tables replace copied declaration blocks where the complete current public contract is already visible.
- Nextra's search implementation, locale switch, theme switch, static route format, and last-updated source remain, styled to the Island hierarchy.

No other parity exception is approved by this matrix.
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

This workspace is the production documentation site. It owns the bilingual landing, guides, component reference, Hook reference, and live audio examples. From the repository root:

The maintained Island → Nextra shell, content, example, API, and intentional-exception contract is recorded in [`ISLAND_PARITY.md`](./ISLAND_PARITY.md).

```bash
pnpm dev:docs
pnpm build:docs
Expand Down
14 changes: 2 additions & 12 deletions docs/app/(landing)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
import Link from 'next/link'
import { RootLanguageRedirect } from './root-language-redirect'

export default function LanguageLandingPage() {
return (
<main className="language-landing">
<p className="language-landing__eyebrow">Echo UI documentation</p>
<h1>Choose your documentation language</h1>
<p>Choose a language to explore the Echo UI guides. 选择文档语言以继续浏览。</p>
<nav aria-label="Documentation languages">
<Link href="/en">English</Link>
<Link href="/zh">简体中文</Link>
</nav>
</main>
)
return <RootLanguageRedirect />
}
25 changes: 25 additions & 0 deletions docs/app/(landing)/root-language-redirect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client'

import { useEffect } from 'react'

const basePath = process.env.NEXT_PUBLIC_DOCS_BASE_PATH ?? ''
const destination = `${basePath}/en/`

export function RootLanguageRedirect() {
useEffect(() => {
window.location.replace(destination)
}, [])

return (
<>
<meta content={`0;url=${destination}`} httpEquiv="refresh" />
<meta content="Choose your documentation language" name="description" />
<link href={`https://echoui.dev${destination}`} rel="canonical" />
<link href={`${basePath}/en/`} hrefLang="en" rel="alternate" />
<link href={`${basePath}/zh/`} hrefLang="zh" rel="alternate" />
<noscript>
This documentation moved to <a href={destination}>{destination}</a>.
</noscript>
</>
)
}
40 changes: 32 additions & 8 deletions docs/app/[lang]/island-theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,11 @@ body {
}

footer {
border: 0 !important;
display: none !important;
background: var(--island-c-bg-alt);
border-top: 1px solid var(--island-c-divider-light) !important;
color: var(--island-c-text-2);
flex-wrap: wrap;
gap: 8px 24px;
}

.island-mobile-menu {
Expand Down Expand Up @@ -275,10 +278,6 @@ aside.nextra-sidebar li.active > a {
font-weight: 400;
}

aside.nextra-sidebar .nextra-sidebar-footer {
display: none;
}

body > div:has(> aside.nextra-sidebar) > article {
box-sizing: border-box;
flex: 0 0 712px;
Expand Down Expand Up @@ -327,7 +326,7 @@ nav.nextra-toc > div > ul a {
}

nav.nextra-toc > div > div:last-child {
display: none;
display: grid;
}

body:has(.echo-home) > div:has(> aside.nextra-sidebar) {
Expand Down Expand Up @@ -414,6 +413,23 @@ article main a:hover {
color: var(--island-c-brand-dark);
}

article main a[target='_blank'] > svg {
display: none;
}

article main .nextra-callout {
background: transparent;
border: 1px solid var(--island-c-divider);
border-radius: 8px;
color: var(--island-c-text-1);
margin: 16px 0;
padding: 16px 20px;
}

article main .nextra-callout > :first-child {
display: none;
}

article main :is(ul, ol) {
margin: 16px 0;
padding-left: 1.25rem;
Expand Down Expand Up @@ -595,6 +611,15 @@ article main table[class] code {
}
}

@media (width >= 768px) {
footer {
padding-left: max(
calc(var(--island-sidebar-width) + 32px),
calc((100vw - 704px) / 2)
) !important;
}
}

@media (width < 768px) {
html:root {
--nextra-navbar-height: var(--island-nav-height-mobile);
Expand Down Expand Up @@ -723,5 +748,4 @@ article main table[class] code {
article main h1 {
font-size: 28px;
}

}
16 changes: 13 additions & 3 deletions docs/app/[lang]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'nextra-theme-docs/style.css'
import type { FC, ReactNode } from 'react'
import '@nafr/echo-ui/style.css'
import { IslandMobileMenu } from '../_components/island-mobile-menu'
import { LegacyVerifierBridge } from '../_components/legacy-verifier-bridge'
import './styles.css'
import './island-theme.css'

Expand All @@ -28,6 +29,8 @@ type LayoutProps = Readonly<{

const messagesByLocale = {
en: {
copyright: 'Copyright © 2023-present leyoonafr',
edit: '📝 Edit this page on GitHub',
footer: 'Released under the MIT License.',
name: 'English',
search: 'Search',
Expand All @@ -42,6 +45,8 @@ const messagesByLocale = {
},
},
zh: {
copyright: 'Copyright © 2023-present leyoonafr',
edit: '📝 在 GitHub 上编辑此页',
footer: '基于 MIT 许可证发布。',
name: '简体中文',
search: '搜索',
Expand Down Expand Up @@ -80,7 +85,12 @@ const RootLayout: FC<LayoutProps> = async ({ children, params }) => {
<Search placeholder={messages.search} />
</div>
)
const footer = <Footer>{messages.footer}</Footer>
const footer = (
<Footer>
<span>{messages.footer}</span>
<span>{messages.copyright}</span>
</Footer>
)

return (
<html lang={lang} dir="ltr" suppressHydrationWarning>
Expand All @@ -92,15 +102,15 @@ const RootLayout: FC<LayoutProps> = async ({ children, params }) => {
}}
/>
<body>
<LegacyVerifierBridge />
<Layout
copyPageButton={false}
docsRepositoryBase="https://github.com/codeacme17/echo-ui/tree/main/docs"
editLink={null}
editLink={messages.edit}
feedback={{ content: null }}
footer={footer}
i18n={locales}
navbar={navbar}
navigation={false}
pageMap={await getPageMap(`/${lang}`)}
search={search}
sidebar={{ toggleButton: false }}
Expand Down
44 changes: 42 additions & 2 deletions docs/app/_components/api-reference.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,25 @@ export type ApiSection = Readonly<{
rows: readonly ApiRow[]
}>

export type DataAttribute = Readonly<{
description: LocalizedText
name: string
values?: string
}>

export const localizedText = (en: string, zh: string): LocalizedText => ({ en, zh })

const labels = {
en: {
attribute: 'Data Attributes',
defaultValue: 'Default',
description: 'Description',
name: 'Prop',
required: 'required',
type: 'Type',
},
zh: {
attribute: 'Data 属性',
defaultValue: '默认值',
description: '说明',
name: '属性',
Expand All @@ -42,6 +50,38 @@ type ApiTableProps = Readonly<{
section: ApiSection
}>

type DataAttributesProps = Readonly<{
attributes: readonly DataAttribute[]
component: string
lang: Locale
}>

export const DataAttributes: FC<DataAttributesProps> = ({ attributes, component, lang }) => {
if (attributes.length === 0) return null

return (
<section data-data-attributes={component}>
<h3>{labels[lang].attribute}</h3>
<dl>
{attributes.map((attribute) => (
<div key={attribute.name}>
<dt>
<code>{attribute.name}</code>
{attribute.values && (
<>
{' '}
— <code>{attribute.values}</code>
</>
)}
</dt>
<dd>{attribute.description[lang]}</dd>
</div>
))}
</dl>
</section>
)
}

export const ApiTable: FC<ApiTableProps> = ({ lang, section }) => {
const localeLabels = labels[lang]

Expand All @@ -60,9 +100,9 @@ export const ApiTable: FC<ApiTableProps> = ({ lang, section }) => {
<thead>
<tr>
<th scope="col">{localeLabels.name}</th>
<th scope="col">{localeLabels.description}</th>
<th scope="col">{localeLabels.type}</th>
<th scope="col">{localeLabels.defaultValue}</th>
<th scope="col">{localeLabels.description}</th>
</tr>
</thead>
<tbody>
Expand All @@ -76,13 +116,13 @@ export const ApiTable: FC<ApiTableProps> = ({ lang, section }) => {
</span>
)}
</td>
<td data-label={localeLabels.description}>{row.description[lang]}</td>
<td data-label={localeLabels.type}>
<code>{row.type}</code>
</td>
<td data-label={localeLabels.defaultValue}>
<code>{row.defaultValue}</code>
</td>
<td data-label={localeLabels.description}>{row.description[lang]}</td>
</tr>
))}
</tbody>
Expand Down
Loading
Loading