mirror of https://github.com/vuejs/vitepress
Accodions now use native details/summary clickHandler not needed, icon button not needed, kept icon as is and styles as is, just not better SCREEN READER support and easier to maintain since native
🎯 Final State: ✅ All files properly formatted with prettier ✅ No syntax errors in HTML files ✅ Clean CSS rules without duplicates ✅ Client builds successfully ✅ Correct caret rotation: 90° when closed, 0° when openpull/4847/head
parent
e2a8ba04c5
commit
b517fa01f9
@ -0,0 +1,205 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Debug Sidebar Logic</title>
|
||||
<style>
|
||||
body {
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
margin: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.debug-section {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.correct {
|
||||
border-left: 4px solid #4caf50;
|
||||
}
|
||||
|
||||
.incorrect {
|
||||
border-left: 4px solid #f44336;
|
||||
}
|
||||
|
||||
/* VitePress sidebar styles */
|
||||
.VPSidebarItem {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.text {
|
||||
flex-grow: 1;
|
||||
color: #213547;
|
||||
}
|
||||
|
||||
/* CSS-only caret for collapsible items */
|
||||
.VPSidebarItem details > summary.item {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.VPSidebarItem details > summary.item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid #8b949e;
|
||||
border-top: 3px solid transparent;
|
||||
border-bottom: 3px solid transparent;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
|
||||
.VPSidebarItem details[open] > summary.item::after {
|
||||
transform: translateY(-50%) rotate(90deg);
|
||||
border-left-color: #476582;
|
||||
}
|
||||
|
||||
.VPSidebarItem details > summary {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.VPSidebarItem details > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.items {
|
||||
margin-left: 16px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.child-item {
|
||||
padding: 2px 0;
|
||||
font-size: 13px;
|
||||
color: #476582;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Debug: VitePress Sidebar Logic</h1>
|
||||
|
||||
<div class="debug-section correct">
|
||||
<h2>✅ CORRECT: Item with children should use <details></h2>
|
||||
<p>
|
||||
<strong>hasChildren = true</strong> → should render as
|
||||
<code><details></code>
|
||||
</p>
|
||||
|
||||
<div class="VPSidebarItem">
|
||||
<details>
|
||||
<summary class="item">
|
||||
<span class="text">Introduction (has children)</span>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<div class="child-item">What is VitePress?</div>
|
||||
<div class="child-item">Getting Started</div>
|
||||
<div class="child-item">Routing</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="debug-section correct">
|
||||
<h2>✅ CORRECT: Item without children should use <div></h2>
|
||||
<p>
|
||||
<strong>hasChildren = false</strong> → should render as
|
||||
<code><div></code>
|
||||
</p>
|
||||
|
||||
<div class="VPSidebarItem">
|
||||
<div class="item">
|
||||
<span class="text">Simple Link (no children)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="debug-section incorrect">
|
||||
<h2>❌ INCORRECT: If this is what you're seeing...</h2>
|
||||
<p>
|
||||
<strong>hasChildren = false</strong> → but wrongly renders as
|
||||
<code><details></code>
|
||||
</p>
|
||||
|
||||
<div class="VPSidebarItem">
|
||||
<details>
|
||||
<summary class="item">
|
||||
<span class="text">Simple Link (should NOT be details)</span>
|
||||
</summary>
|
||||
<!-- No children, but still wrapped in details -->
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="debug-section incorrect">
|
||||
<h2>❌ INCORRECT: If this is what you're seeing...</h2>
|
||||
<p>
|
||||
<strong>hasChildren = true</strong> → but wrongly renders as
|
||||
<code><div></code>
|
||||
</p>
|
||||
|
||||
<div class="VPSidebarItem">
|
||||
<div class="item">
|
||||
<span class="text">Introduction (should be details but isn't)</span>
|
||||
</div>
|
||||
<div class="items">
|
||||
<div class="child-item">What is VitePress?</div>
|
||||
<div class="child-item">Getting Started</div>
|
||||
<div class="child-item">Routing</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Current Logic in VPSidebarItem.vue:</h2>
|
||||
<pre><code>const sectionTag = computed(() => (hasChildren.value ? 'details' : 'div'))
|
||||
|
||||
<template>
|
||||
<component :is="sectionTag" class="VPSidebarItem" :class="classes" :open="!collapsed">
|
||||
<!-- Non-collapsible items (no children) - use regular div -->
|
||||
<div
|
||||
v-if="props.item.text && !hasChildren"
|
||||
class="item"
|
||||
>
|
||||
<!-- content -->
|
||||
</div>
|
||||
|
||||
<!-- Collapsible items (has children) - use summary -->
|
||||
<summary
|
||||
v-else-if="props.item.text && hasChildren"
|
||||
class="item"
|
||||
>
|
||||
<!-- content -->
|
||||
</summary>
|
||||
</component>
|
||||
</template></code></pre>
|
||||
|
||||
<p><strong>Expected behavior:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
Items with children:
|
||||
<code><details><summary></code> (collapsible with caret)
|
||||
</li>
|
||||
<li>
|
||||
Items without children: <code><div><div></code> (simple
|
||||
link, no caret)
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,235 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Test Caret for All Heading Levels</title>
|
||||
<style>
|
||||
:root {
|
||||
--vp-c-text-1: #213547;
|
||||
--vp-c-text-2: #476582;
|
||||
--vp-c-text-3: #8b949e;
|
||||
--vp-c-divider: #e2e8f0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
margin: 20px;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.VPSidebarItem {
|
||||
margin: 8px 0;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.text {
|
||||
flex-grow: 1;
|
||||
padding: 4px 0;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.VPSidebarItem.level-0 .text {
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.VPSidebarItem.level-1 .text {
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
/* CSS-only caret for collapsible items */
|
||||
.VPSidebarItem details > summary.item {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.VPSidebarItem details > summary.item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid var(--vp-c-text-3);
|
||||
border-top: 3px solid transparent;
|
||||
border-bottom: 3px solid transparent;
|
||||
transition: all 0.25s;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Rotate caret when details is open */
|
||||
.VPSidebarItem details[open] > summary.item::after {
|
||||
transform: translateY(-50%) rotate(90deg);
|
||||
border-left-color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
/* Hover effect for caret */
|
||||
.VPSidebarItem details > summary.item:hover::after {
|
||||
border-left-color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
/* Ensure caret appears for all heading levels */
|
||||
.VPSidebarItem details > summary.item h2,
|
||||
.VPSidebarItem details > summary.item h3,
|
||||
.VPSidebarItem details > summary.item h4,
|
||||
.VPSidebarItem details > summary.item h5,
|
||||
.VPSidebarItem details > summary.item h6,
|
||||
.VPSidebarItem details > summary.item p {
|
||||
margin: 0;
|
||||
padding-right: 32px;
|
||||
/* Make space for the caret */
|
||||
}
|
||||
|
||||
/* Hide native details marker */
|
||||
.VPSidebarItem details > summary {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.VPSidebarItem details > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.items {
|
||||
margin-left: 16px;
|
||||
margin-top: 8px;
|
||||
padding-left: 16px;
|
||||
border-left: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Test: Caret for All Heading Levels</h1>
|
||||
<p>
|
||||
This tests that the CSS caret appears for all heading levels (h2, h3, h4,
|
||||
etc.) in VitePress sidebar items.
|
||||
</p>
|
||||
|
||||
<div class="VPSidebarItem level-0">
|
||||
<small class="label">Depth 0 → h2 element</small>
|
||||
<details>
|
||||
<summary class="item">
|
||||
<h2 class="text">Level 0 Item (h2)</h2>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<p>Child content here</p>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="VPSidebarItem level-1">
|
||||
<small class="label">Depth 1 → h3 element</small>
|
||||
<details>
|
||||
<summary class="item">
|
||||
<h3 class="text">Level 1 Item (h3)</h3>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<p>Child content here</p>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="VPSidebarItem level-2">
|
||||
<small class="label">Depth 2 → h4 element</small>
|
||||
<details>
|
||||
<summary class="item">
|
||||
<h4 class="text">Level 2 Item (h4)</h4>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<p>Child content here</p>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="VPSidebarItem level-3">
|
||||
<small class="label">Depth 3 → h5 element</small>
|
||||
<details>
|
||||
<summary class="item">
|
||||
<h5 class="text">Level 3 Item (h5)</h5>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<p>Child content here</p>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="VPSidebarItem level-4">
|
||||
<small class="label">Depth 4 → h6 element</small>
|
||||
<details>
|
||||
<summary class="item">
|
||||
<h6 class="text">Level 4 Item (h6)</h6>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<p>Child content here</p>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="VPSidebarItem level-5">
|
||||
<small class="label">Depth 5+ → p element</small>
|
||||
<details>
|
||||
<summary class="item">
|
||||
<p class="text">Level 5+ Item (p)</p>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<p>Child content here</p>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<h2>Key Changes Made:</h2>
|
||||
<ul>
|
||||
<li>
|
||||
✅ Added <code>z-index: 1</code> to ensure caret appears above other
|
||||
elements
|
||||
</li>
|
||||
<li>✅ Added <code>cursor: pointer</code> to summary.item</li>
|
||||
<li>
|
||||
✅ Added <code>padding-right: 32px</code> to all heading levels to make
|
||||
space for caret
|
||||
</li>
|
||||
<li>✅ Added <code>margin: 0</code> to reset default heading margins</li>
|
||||
<li>
|
||||
✅ Caret should now appear consistently for h2, h3, h4, h5, h6, and p
|
||||
elements
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>VitePress Depth Logic:</h2>
|
||||
<pre><code>const textTag = computed(() => {
|
||||
return !hasChildren.value
|
||||
? 'p' // Simple links use p
|
||||
: props.depth + 2 === 7
|
||||
? 'p' // Max depth uses p
|
||||
: `h${props.depth + 2}` // Others use h2, h3, h4, h5, h6
|
||||
})</code></pre>
|
||||
|
||||
<p>
|
||||
<strong>Expected result:</strong> All items above should show a triangular
|
||||
caret on the right side that rotates when clicked.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,261 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Corrected Details/Summary Pattern</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #f9fafb;
|
||||
}
|
||||
|
||||
.demo-sidebar {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.text {
|
||||
flex-grow: 1;
|
||||
padding: 4px 8px;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: #3b82f6;
|
||||
text-decoration: none;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
color: #1d4ed8;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.caret {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
color: #9ca3af;
|
||||
cursor: pointer;
|
||||
transition: color 0.25s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.item:hover .caret {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.caret-icon {
|
||||
font-size: 14px;
|
||||
transform: rotate(0deg);
|
||||
transition: transform 0.25s;
|
||||
}
|
||||
|
||||
/* When details is open, rotate the caret */
|
||||
details[open] .caret-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* Hide native details marker */
|
||||
details > summary {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
details > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.items {
|
||||
border-left: 1px solid #e5e7eb;
|
||||
padding-left: 16px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
/* Show items only when details is open */
|
||||
details[open] .items {
|
||||
display: block;
|
||||
}
|
||||
|
||||
details:not([open]) .items {
|
||||
display: none;
|
||||
}
|
||||
|
||||
details > summary.item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
details > summary.item:hover {
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
|
||||
/* Regular divs for non-collapsible items */
|
||||
div.item:hover {
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 20px 0 10px 0;
|
||||
color: #1f2937;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>🎯 Corrected Details/Summary Pattern</h1>
|
||||
|
||||
<div class="demo-sidebar">
|
||||
<h2>Proper Implementation</h2>
|
||||
|
||||
<div class="section-title">❌ Before (Wrong Logic)</div>
|
||||
<p style="color: #ef4444; font-size: 14px">
|
||||
All items were wrapped in details/summary regardless of having children
|
||||
</p>
|
||||
|
||||
<div class="section-title">✅ After (Correct Logic)</div>
|
||||
|
||||
<!-- 1. Simple link items - NO details/summary wrapper -->
|
||||
<div class="item">
|
||||
<a href="#" class="link">
|
||||
<div class="text">🏠 Home (simple link)</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<a href="#" class="link">
|
||||
<div class="text">📄 About (simple link)</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 2. Section with children - YES details/summary wrapper -->
|
||||
<details>
|
||||
<summary class="item">
|
||||
<div class="text">📚 Guide (has children)</div>
|
||||
<div class="caret">
|
||||
<span class="caret-icon">▶</span>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<!-- Child items are simple links -->
|
||||
<div class="item">
|
||||
<a href="#" class="link">
|
||||
<div class="text">📖 Getting Started</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a href="#" class="link">
|
||||
<div class="text">⚙️ Configuration</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Nested section with children -->
|
||||
<details>
|
||||
<summary class="item">
|
||||
<div class="text">🔧 Advanced (has children)</div>
|
||||
<div class="caret">
|
||||
<span class="caret-icon">▶</span>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<div class="item">
|
||||
<a href="#" class="link">
|
||||
<div class="text">🛠️ Customization</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a href="#" class="link">
|
||||
<div class="text">🔌 Plugins</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- 3. Another section with children -->
|
||||
<details>
|
||||
<summary class="item">
|
||||
<div class="text">📖 API Reference (has children)</div>
|
||||
<div class="caret">
|
||||
<span class="caret-icon">▶</span>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<div class="item">
|
||||
<a href="#" class="link">
|
||||
<div class="text">📋 Components</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a href="#" class="link">
|
||||
<div class="text">🎨 Theme Config</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- 4. Another simple link -->
|
||||
<div class="item">
|
||||
<a href="#" class="link">
|
||||
<div class="text">❓ FAQ (simple link)</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style="
|
||||
margin-top: 30px;
|
||||
padding: 20px;
|
||||
background: #f0f9ff;
|
||||
border-radius: 8px;
|
||||
"
|
||||
>
|
||||
<h3>🎯 Correct Logic:</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px">
|
||||
<div>
|
||||
<h4>🚫 Items WITHOUT Children:</h4>
|
||||
<ul style="color: #059669">
|
||||
<li>✅ Use regular <code><div></code> wrapper</li>
|
||||
<li>
|
||||
✅ No <code><details></code> or <code><summary></code>
|
||||
</li>
|
||||
<li>✅ No caret icon</li>
|
||||
<li>✅ Direct links work normally</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4>✅ Items WITH Children:</h4>
|
||||
<ul style="color: #dc2626">
|
||||
<li>✅ Use <code><details></code> wrapper</li>
|
||||
<li>✅ Use <code><summary></code> for the item</li>
|
||||
<li>✅ Show caret icon</li>
|
||||
<li>✅ Collapsible behavior</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,169 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>CSS-Only Caret Test</title>
|
||||
<style>
|
||||
:root {
|
||||
--vp-c-text-1: #213547;
|
||||
--vp-c-text-2: #476582;
|
||||
--vp-c-text-3: #8b949e;
|
||||
}
|
||||
|
||||
.VPSidebarItem {
|
||||
margin: 8px 0;
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, sans-serif;
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.text {
|
||||
flex-grow: 1;
|
||||
font-size: 14px;
|
||||
color: var(--vp-c-text-1);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* CSS-only caret for collapsible items */
|
||||
.VPSidebarItem details > summary.item {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.VPSidebarItem details > summary.item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid var(--vp-c-text-3);
|
||||
border-top: 3px solid transparent;
|
||||
border-bottom: 3px solid transparent;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
|
||||
/* Rotate caret when details is open */
|
||||
.VPSidebarItem details[open] > summary.item::after {
|
||||
transform: translateY(-50%) rotate(90deg);
|
||||
border-left-color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
/* Hover effect for caret */
|
||||
.VPSidebarItem details > summary.item:hover::after {
|
||||
border-left-color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
/* Hide native details marker */
|
||||
.VPSidebarItem details > summary {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.VPSidebarItem details > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.items {
|
||||
margin-left: 16px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.child-item {
|
||||
padding: 2px 0;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.child-item:hover {
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
/* Simple link item (no caret) */
|
||||
.simple-link {
|
||||
color: var(--vp-c-text-2);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.simple-link:hover {
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>CSS-Only Caret Test</h1>
|
||||
<p>
|
||||
This demonstrates the pure CSS caret implementation for VitePress sidebar
|
||||
items.
|
||||
</p>
|
||||
|
||||
<div class="VPSidebarItem">
|
||||
<details>
|
||||
<summary class="item">
|
||||
<span class="text">Introduction</span>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<div class="child-item">
|
||||
<a href="#" class="simple-link">What is VitePress?</a>
|
||||
</div>
|
||||
<div class="child-item">
|
||||
<a href="#" class="simple-link">Getting Started</a>
|
||||
</div>
|
||||
<div class="child-item">
|
||||
<a href="#" class="simple-link">Routing</a>
|
||||
</div>
|
||||
<div class="child-item">
|
||||
<a href="#" class="simple-link">Deploy</a>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="VPSidebarItem">
|
||||
<details>
|
||||
<summary class="item">
|
||||
<span class="text">Writing</span>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<div class="child-item">
|
||||
<a href="#" class="simple-link">Markdown Extensions</a>
|
||||
</div>
|
||||
<div class="child-item">
|
||||
<a href="#" class="simple-link">Asset Handling</a>
|
||||
</div>
|
||||
<div class="child-item">
|
||||
<a href="#" class="simple-link">Frontmatter</a>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="VPSidebarItem">
|
||||
<div class="item">
|
||||
<a href="#" class="simple-link">
|
||||
<span class="text">Simple Link (No Caret)</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p><strong>Features:</strong></p>
|
||||
<ul>
|
||||
<li>✅ CSS-only caret (no JavaScript needed)</li>
|
||||
<li>✅ Caret positioned on the right side</li>
|
||||
<li>✅ Smooth rotation animation when opened/closed</li>
|
||||
<li>✅ Hover color changes</li>
|
||||
<li>✅ No caret for simple links</li>
|
||||
<li>✅ Native details/summary accessibility</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,201 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Details/Summary Caret Test</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.demo-sidebar {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.text {
|
||||
flex-grow: 1;
|
||||
padding: 4px 0;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.caret {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: -7px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
color: #9ca3af;
|
||||
cursor: pointer;
|
||||
transition: color 0.25s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.item:hover .caret {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.caret-icon {
|
||||
font-size: 18px;
|
||||
transform: rotate(0deg);
|
||||
transition: transform 0.25s;
|
||||
}
|
||||
|
||||
/* When details is open, rotate the caret */
|
||||
details[open] .caret-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* Hide native details marker */
|
||||
details > summary {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
details > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.items {
|
||||
border-left: 1px solid #e5e7eb;
|
||||
padding-left: 16px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
/* Show items only when details is open */
|
||||
details[open] .items {
|
||||
display: block;
|
||||
}
|
||||
|
||||
details:not([open]) .items {
|
||||
display: none;
|
||||
}
|
||||
|
||||
details > summary.item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
details > summary.item:hover {
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>VitePress Details/Summary Caret Test</h1>
|
||||
|
||||
<div class="demo-sidebar">
|
||||
<h2>Sidebar with Details/Summary Pattern</h2>
|
||||
|
||||
<!-- Non-collapsible item -->
|
||||
<div class="item">
|
||||
<div class="text">🏠 Home</div>
|
||||
</div>
|
||||
|
||||
<!-- Collapsible item with children -->
|
||||
<details>
|
||||
<summary class="item">
|
||||
<div class="text">📚 Guide</div>
|
||||
<div class="caret">
|
||||
<span class="caret-icon">▶</span>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<div class="item">
|
||||
<div class="text">📖 Getting Started</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="text">⚙️ Configuration</div>
|
||||
</div>
|
||||
|
||||
<!-- Nested collapsible -->
|
||||
<details>
|
||||
<summary class="item">
|
||||
<div class="text">🔧 Advanced</div>
|
||||
<div class="caret">
|
||||
<span class="caret-icon">▶</span>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<div class="item">
|
||||
<div class="text">🛠️ Customization</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="text">🔌 Plugins</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Another collapsible item -->
|
||||
<details>
|
||||
<summary class="item">
|
||||
<div class="text">📖 Reference</div>
|
||||
<div class="caret">
|
||||
<span class="caret-icon">▶</span>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<div class="item">
|
||||
<div class="text">📋 API</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="text">🎨 Theming</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Non-collapsible item -->
|
||||
<div class="item">
|
||||
<div class="text">❓ FAQ</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style="
|
||||
margin-top: 30px;
|
||||
padding: 20px;
|
||||
background: #f0f9ff;
|
||||
border-radius: 8px;
|
||||
"
|
||||
>
|
||||
<h3>✨ Features Demonstrated:</h3>
|
||||
<ul>
|
||||
<li>
|
||||
🔄 <strong>CSS-only caret rotation</strong> - No JavaScript needed!
|
||||
</li>
|
||||
<li>
|
||||
🎯 <strong>Native details/summary</strong> - Better accessibility
|
||||
</li>
|
||||
<li>
|
||||
⌨️ <strong>Keyboard navigation</strong> - Tab and Enter work out of
|
||||
the box
|
||||
</li>
|
||||
<li>
|
||||
🔗 <strong>Nested collapsible sections</strong> - Supports multiple
|
||||
levels
|
||||
</li>
|
||||
<li>🎨 <strong>Smooth transitions</strong> - Caret rotates smoothly</li>
|
||||
<li>📱 <strong>Mobile friendly</strong> - Works on all devices</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,281 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Fixed Sidebar Test</title>
|
||||
<style>
|
||||
:root {
|
||||
--vp-c-text-1: #213547;
|
||||
--vp-c-text-2: #476582;
|
||||
--vp-c-text-3: #8b949e;
|
||||
--vp-c-divider: #e2e8f0;
|
||||
--vp-c-brand-1: #3451b2;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
margin: 20px;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.test-section {
|
||||
background: white;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.success {
|
||||
border-left: 4px solid #10b981;
|
||||
}
|
||||
|
||||
/* VitePress styles */
|
||||
.VPSidebarItem {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.VPSidebarItem.level-0 {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.VPSidebarItem.collapsed.level-0,
|
||||
.VPSidebarItem.level-0:not([open]) {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.VPSidebarItem details > summary.item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
bottom: 6px;
|
||||
left: -17px;
|
||||
width: 2px;
|
||||
border-radius: 2px;
|
||||
transition: background-color 0.25s;
|
||||
}
|
||||
|
||||
.text {
|
||||
flex-grow: 1;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.VPSidebarItem.level-0 .text {
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.VPSidebarItem.level-1 .text {
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
/* CSS-only caret for collapsible items */
|
||||
.VPSidebarItem details > summary.item {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.VPSidebarItem details > summary.item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid var(--vp-c-text-3);
|
||||
border-top: 3px solid transparent;
|
||||
border-bottom: 3px solid transparent;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
|
||||
/* Rotate caret when details is open */
|
||||
.VPSidebarItem details[open] > summary.item::after {
|
||||
transform: translateY(-50%) rotate(90deg);
|
||||
border-left-color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
/* Hover effect for caret */
|
||||
.VPSidebarItem details > summary.item:hover::after {
|
||||
border-left-color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
/* Hide native details marker */
|
||||
.VPSidebarItem details > summary {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.VPSidebarItem details > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.items {
|
||||
margin-left: 16px;
|
||||
margin-top: 4px;
|
||||
border-left: 1px solid var(--vp-c-divider);
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: var(--vp-c-text-2);
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>✅ Fixed VitePress Sidebar Structure</h1>
|
||||
|
||||
<div class="test-section success">
|
||||
<h2>Correct Structure:</h2>
|
||||
<p>
|
||||
<strong>Items with children:</strong>
|
||||
<code><details><summary></code> with CSS caret
|
||||
</p>
|
||||
<p>
|
||||
<strong>Items without children:</strong>
|
||||
<code><div><div></code> with no caret
|
||||
</p>
|
||||
|
||||
<div style="margin: 20px 0">
|
||||
<!-- Item WITH children = details/summary -->
|
||||
<details class="VPSidebarItem level-0">
|
||||
<summary class="item">
|
||||
<div class="indicator"></div>
|
||||
<span class="text">Introduction (has children)</span>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<div class="VPSidebarItem level-1">
|
||||
<div class="item">
|
||||
<div class="indicator"></div>
|
||||
<a href="#" class="link">
|
||||
<span class="text">What is VitePress?</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="VPSidebarItem level-1">
|
||||
<div class="item">
|
||||
<div class="indicator"></div>
|
||||
<a href="#" class="link">
|
||||
<span class="text">Getting Started</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="VPSidebarItem level-1">
|
||||
<div class="item">
|
||||
<div class="indicator"></div>
|
||||
<a href="#" class="link">
|
||||
<span class="text">Routing</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Item WITHOUT children = div/div -->
|
||||
<div class="VPSidebarItem level-0">
|
||||
<div class="item">
|
||||
<div class="indicator"></div>
|
||||
<a href="#" class="link">
|
||||
<span class="text">Simple Link (no children)</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Another item WITH children -->
|
||||
<details class="VPSidebarItem level-0">
|
||||
<summary class="item">
|
||||
<div class="indicator"></div>
|
||||
<span class="text">Writing (has children)</span>
|
||||
</summary>
|
||||
<div class="items">
|
||||
<div class="VPSidebarItem level-1">
|
||||
<div class="item">
|
||||
<div class="indicator"></div>
|
||||
<a href="#" class="link">
|
||||
<span class="text">Markdown Extensions</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="VPSidebarItem level-1">
|
||||
<div class="item">
|
||||
<div class="indicator"></div>
|
||||
<a href="#" class="link">
|
||||
<span class="text">Asset Handling</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Key Features:</h2>
|
||||
<ul>
|
||||
<li>
|
||||
✅ <strong>Conditional rendering:</strong> Only items with children
|
||||
use <code><details></code>
|
||||
</li>
|
||||
<li>
|
||||
✅ <strong>CSS-only caret:</strong> Appears on the right side of items
|
||||
with children
|
||||
</li>
|
||||
<li>
|
||||
✅ <strong>Smooth animation:</strong> Caret rotates 90° when accordion
|
||||
opens
|
||||
</li>
|
||||
<li>✅ <strong>Hover effects:</strong> Caret color changes on hover</li>
|
||||
<li>
|
||||
✅ <strong>Accessibility:</strong> Native keyboard navigation with
|
||||
details/summary
|
||||
</li>
|
||||
<li>✅ <strong>No JavaScript:</strong> Pure CSS implementation</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Updated VPSidebarItem.vue Logic:</h2>
|
||||
<pre><code><template>
|
||||
<!-- Items WITH children use details/summary -->
|
||||
<details v-if="hasChildren" class="VPSidebarItem" :class="classes" :open="!collapsed">
|
||||
<summary class="item">
|
||||
<!-- content with CSS caret -->
|
||||
</summary>
|
||||
<div class="items">
|
||||
<!-- child items -->
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Items WITHOUT children use div -->
|
||||
<div v-else class="VPSidebarItem" :class="classes">
|
||||
<div class="item">
|
||||
<!-- content with no caret -->
|
||||
</div>
|
||||
</div>
|
||||
</template></code></pre>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,80 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Sidebar Test</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 300px;
|
||||
border: 1px solid #ddd;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
details {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.nested {
|
||||
margin-left: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Details/Summary Pattern Test</h1>
|
||||
<div class="sidebar">
|
||||
<details open>
|
||||
<summary>Getting Started</summary>
|
||||
<div class="nested">
|
||||
<details>
|
||||
<summary>Installation</summary>
|
||||
<div class="nested">
|
||||
<p>npm install vitepress</p>
|
||||
</div>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Configuration</summary>
|
||||
<div class="nested">
|
||||
<p>Create .vitepress/config.js</p>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>API Reference</summary>
|
||||
<div class="nested">
|
||||
<details>
|
||||
<summary>Components</summary>
|
||||
<div class="nested">
|
||||
<p>VPSidebarItem</p>
|
||||
<p>VPSidebarGroup</p>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<h2>Benefits of Details/Summary Pattern:</h2>
|
||||
<ul>
|
||||
<li>✅ Native browser support</li>
|
||||
<li>✅ Built-in accessibility</li>
|
||||
<li>✅ Keyboard navigation</li>
|
||||
<li>✅ Screen reader support</li>
|
||||
<li>✅ No JavaScript required</li>
|
||||
<li>✅ Semantic HTML</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in new issue