diff --git a/lib/rdoc/generator/darkfish.rb b/lib/rdoc/generator/darkfish.rb
index 7fec36500f..9679216b95 100644
--- a/lib/rdoc/generator/darkfish.rb
+++ b/lib/rdoc/generator/darkfish.rb
@@ -314,6 +314,8 @@ def generate_index
# Generates a class file for +klass+
def generate_class(klass, template_file = nil)
+ # This is used to auto-collapse Pages section on class/module pages
+ @inside_class_file = true
current = klass
template_file ||= @template_dir + 'class.rhtml'
@@ -338,6 +340,8 @@ def generate_class(klass, template_file = nil)
here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
here
end
+ ensure
+ @inside_class_file = false
end
##
@@ -764,7 +768,7 @@ def generate_class_index_content(classes, rel_prefix)
end
def traverse_classes(klasses, grouped_classes, rel_prefix, solo = false)
- content = +'
'
+ content = +''
klasses.each do |index_klass|
if children = grouped_classes[index_klass.full_name]
diff --git a/lib/rdoc/generator/template/aliki/_icons.rhtml b/lib/rdoc/generator/template/aliki/_icons.rhtml
new file mode 100644
index 0000000000..9c11a4a120
--- /dev/null
+++ b/lib/rdoc/generator/template/aliki/_icons.rhtml
@@ -0,0 +1,208 @@
+
diff --git a/lib/rdoc/generator/template/aliki/_sidebar_ancestors.rhtml b/lib/rdoc/generator/template/aliki/_sidebar_ancestors.rhtml
index 497b4a669a..e598b25160 100644
--- a/lib/rdoc/generator/template/aliki/_sidebar_ancestors.rhtml
+++ b/lib/rdoc/generator/template/aliki/_sidebar_ancestors.rhtml
@@ -1,6 +1,16 @@
<%- if klass.type == 'class' && (ancestors = klass.super_classes).any? %>
-
Ancestors
- <%= generate_ancestor_list(ancestors, klass) %>
+
+
+ <%= generate_ancestor_list(ancestors, klass) %>
+
<%- end %>
diff --git a/lib/rdoc/generator/template/aliki/_sidebar_classes.rhtml b/lib/rdoc/generator/template/aliki/_sidebar_classes.rhtml
index d33ecd43f7..11ff53e1de 100644
--- a/lib/rdoc/generator/template/aliki/_sidebar_classes.rhtml
+++ b/lib/rdoc/generator/template/aliki/_sidebar_classes.rhtml
@@ -1,5 +1,15 @@
-
Class and Module Index
+
+
- <%= generate_class_index_content(@classes, rel_prefix) %>
+ <%= generate_class_index_content(@classes, rel_prefix) %>
+
diff --git a/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml b/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml
index d6ce231f03..1544b83778 100644
--- a/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml
+++ b/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml
@@ -1,15 +1,25 @@
<%- unless klass.extends.empty? then %>
-
Extended With Modules
+
+
-
- <%- klass.extends.each do |ext| %>
- <%- unless String === ext.module then %>
- - <%= ext.module.full_name %>
- <%- else %>
- - <%= ext.name %>
- <%- end %>
- <%- end %>
-
+
+ <%- klass.extends.each do |ext| %>
+ <%- unless String === ext.module then %>
+ - <%= ext.module.full_name %>
+ <%- else %>
+ - <%= ext.name %>
+ <%- end %>
+ <%- end %>
+
+
<%- end %>
diff --git a/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml b/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml
index cc2255f861..3a293da0d3 100644
--- a/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml
+++ b/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml
@@ -1,15 +1,25 @@
<%- unless klass.includes.empty? then %>
-
Included Modules
+
+
-
- <%- klass.includes.each do |inc| %>
- <%- unless String === inc.module then %>
- - <%= inc.module.full_name %>
- <%- else %>
- - <%= inc.name %>
- <%- end %>
- <%- end %>
-
+
+ <%- klass.includes.each do |inc| %>
+ <%- unless String === inc.module then %>
+ - <%= inc.module.full_name %>
+ <%- else %>
+ - <%= inc.name %>
+ <%- end %>
+ <%- end %>
+
+
<%- end %>
diff --git a/lib/rdoc/generator/template/aliki/_sidebar_methods.rhtml b/lib/rdoc/generator/template/aliki/_sidebar_methods.rhtml
index 77b361bed1..443aba37eb 100644
--- a/lib/rdoc/generator/template/aliki/_sidebar_methods.rhtml
+++ b/lib/rdoc/generator/template/aliki/_sidebar_methods.rhtml
@@ -1,21 +1,41 @@
<% if (class_methods = klass.class_method_list.sort).any? %>
-
Class Methods
-
- <%- class_methods.each do |meth| %>
- - class="calls-super" <%- end %>><%= h meth.name -%>
- <%- end %>
-
+
+
+
+ <%- class_methods.each do |meth| %>
+ - class="calls-super" <%- end %>><%= h meth.name -%>
+ <%- end %>
+
+
<% end %>
<% if (instance_methods = klass.instance_methods.sort).any? %>
-
Instance Methods
-
- <%- instance_methods.each do |meth| %>
- - class="calls-super" <%- end %>><%= h meth.name -%>
- <%- end %>
-
+
+
+
+ <%- instance_methods.each do |meth| %>
+ - class="calls-super" <%- end %>><%= h meth.name -%>
+ <%- end %>
+
+
<% end %>
diff --git a/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml b/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml
index 2ab60e8552..2cb46ed517 100644
--- a/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml
+++ b/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml
@@ -1,37 +1,67 @@
<%- simple_files = @files.select { |f| f.text? } %>
+
<%- if defined?(current) && current.respond_to?(:page_name) %>
<%- dir = current.full_name[%r{\A[^/]+(?=/)}] || current.page_name %>
<%- end %>
+
<%- unless simple_files.empty? then %>
-
Pages
-
-
- <%- grouped_files = simple_files.group_by { |f| f.full_name[%r{\A[^/]+(?=/)}] || f.page_name } %>
- <%- grouped_files.each do |n, files| %>
- <%- f = files.shift %>
- <%- if files.empty? %>
- - <%= h f.page_name %>
- <%- next %>
- <%- end %>
- -
-
>
-
- <% if n == f.page_name %>
- <%= h n %>
- <% else %>
- <%= h n %>
- <% files.unshift(f) %>
- <% end %>
-
-
- <%- files.each do |f| %>
- - <%= h f.page_name %>
+ >
+
+
+
+ <%- grouped_files = simple_files.group_by { |f| f.full_name[%r{\A[^/]+(?=/)}] || f.page_name } %>
+
+ <%- grouped_files.each do |n, files| %>
+ <%- f = files.shift %>
+
+ <%- if files.empty? %>
+ -
+
+ <%= h f.page_name %>
+
+
+ <%- next %>
<%- end %>
-
-
-
- <%- end %>
-
+
+
-
+
>
+
+ <% if n == f.page_name %>
+ <%= h n %>
+ <% else %>
+ <%= h n %>
+ <% files.unshift(f) %>
+ <% end %>
+
+
+
+
+
+ <%- end %>
+
+
<%- end %>
diff --git a/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml b/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml
index b275657c8e..9ff56e6f60 100644
--- a/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml
+++ b/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml
@@ -1,11 +1,21 @@
<%- unless klass.sections.length == 1 then %>
<%- end %>
diff --git a/lib/rdoc/generator/template/aliki/class.rhtml b/lib/rdoc/generator/template/aliki/class.rhtml
index 9bdaa626dc..a190ff9659 100644
--- a/lib/rdoc/generator/template/aliki/class.rhtml
+++ b/lib/rdoc/generator/template/aliki/class.rhtml
@@ -1,4 +1,5 @@
+<%= render '_icons.rhtml' %>
<%= render '_header.rhtml' %>
<%= render '_sidebar_toggle.rhtml' %>
diff --git a/lib/rdoc/generator/template/aliki/css/rdoc.css b/lib/rdoc/generator/template/aliki/css/rdoc.css
index a059160351..02c228203b 100644
--- a/lib/rdoc/generator/template/aliki/css/rdoc.css
+++ b/lib/rdoc/generator/template/aliki/css/rdoc.css
@@ -146,8 +146,8 @@
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 10%), 0 8px 10px -6px rgb(0 0 0 / 10%);
/* Layout Dimensions */
- --layout-sidebar-width: 280px;
- --layout-sidebar-width-min: 240px;
+ --layout-sidebar-width: 300px;
+ --layout-sidebar-width-min: 300px;
--layout-sidebar-width-max: 15%;
--layout-toc-width-min: 240px;
--layout-toc-width-max: 18%;
@@ -228,13 +228,13 @@ body {
/* Grid layout with header, sidebar, main, and footer */
display: grid;
- grid-template: "header header" var(--layout-header-height) "nav main" 1fr "nav footer" auto / minmax(var(--layout-sidebar-width-min), var(--layout-sidebar-width-max)) 1fr;
+ grid-template: "header header" var(--layout-header-height) "nav main" 1fr "nav footer" auto / var(--layout-sidebar-width) 1fr;
min-height: 100vh;
}
/* Three-column layout when TOC is present */
body.has-toc {
- grid-template: "header header header" var(--layout-header-height) "nav main toc" 1fr "nav footer toc" auto / minmax(var(--layout-sidebar-width-min), var(--layout-sidebar-width-max)) 1fr minmax(var(--layout-toc-width-min), var(--layout-toc-width-max));
+ grid-template: "header header header" var(--layout-header-height) "nav main toc" 1fr "nav footer toc" auto / var(--layout-sidebar-width) 1fr minmax(var(--layout-toc-width-min), var(--layout-toc-width-max));
min-height: 100vh;
}
@@ -673,31 +673,148 @@ nav footer a {
}
}
-nav ul li details > summary {
- list-style: none; /* Remove the default marker */
- position: relative; /* So that the open/close triangle can position itself absolutely inside */
+/*
+ * Shared Collapsible Animation using ::details-content pseudo-element.
+ * This is the modern CSS approach for animating elements.
+ * Uses block-size animation with interpolate-size for smooth height transitions.
+ * Both nav-section-collapsible and nested link-list details share this pattern.
+ */
+nav details {
+ interpolate-size: allow-keywords;
}
-nav ul li details > summary::-webkit-details-marker {
- display: none; /* Removes the default marker, in Safari 18. */
+nav details::details-content {
+ overflow: hidden;
+ block-size: 0;
+ transition: block-size 200ms ease, content-visibility 200ms ease allow-discrete;
}
-nav ul li details > summary::after {
- content: '▶'; /* Unicode right-pointing triangle */
- position: absolute;
- font-size: 0.8em;
- bottom: 0.1em;
- margin-left: 0.3em;
+nav details[open]::details-content {
+ block-size: auto;
+}
+
+/* Collapsible Navigation Section Headers */
+
+nav .nav-section-header {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ padding: var(--space-3) 0;
+ cursor: pointer;
+ list-style: none;
+ user-select: none;
+ -webkit-user-select: none;
+ border-bottom: 1px solid var(--color-border-default);
+ margin-bottom: var(--space-3);
+ transition: color var(--transition-fast);
+}
+
+nav .nav-section-header::-webkit-details-marker {
+ display: none;
+}
+
+nav .nav-section-header:hover {
+ color: var(--color-accent-primary);
+}
+
+nav .nav-section-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 1.25rem;
+ height: 1.25rem;
+ flex-shrink: 0;
+ color: var(--color-accent-primary);
+}
+
+nav .nav-section-icon svg {
+ width: 100%;
+ height: 100%;
+}
+
+nav .nav-section-title {
+ font-size: var(--font-size-base);
+ font-weight: var(--font-weight-semibold);
+ color: inherit;
+}
+
+nav .nav-section-chevron {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 1rem;
+ height: 1rem;
+ flex-shrink: 0;
+ color: var(--color-text-tertiary);
transition: transform var(--transition-base);
}
-nav ul li details[open] > summary::after {
- transform: rotate(90deg); /* Rotate the triangle when open */
+nav .nav-section-chevron svg {
+ width: 100%;
+ height: 100%;
}
-/* Global link hover state is defined after navigation to keep specificity ordering */
-a:hover {
- color: var(--color-link-hover);
+/* Rotate chevron when open */
+nav .nav-section-collapsible[open] > .nav-section-header .nav-section-chevron {
+ transform: rotate(90deg);
+}
+
+nav .nav-section-collapsible > ul,
+nav .nav-section-collapsible > dl,
+nav .nav-section-collapsible > p {
+ margin-top: 0;
+}
+
+nav .nav-section-collapsible > .nav-list {
+ padding-left: var(--space-5);
+ border-left: 1px solid var(--color-border-subtle);
+ margin-left: 9px; /* Align with the section icon center */
+}
+
+nav .nav-section-collapsible .nav-list .link-list {
+ border-left: none;
+ margin-left: 0;
+ padding-left: var(--space-5);
+}
+
+/*
+ Improve chevron styling for details under link-list, using SVG chevron that matches nav-section-chevron
+ We need to avoid adding the element in class content generation so it doesn't break darkfish styles
+*/
+nav li details:has(.link-list) > summary {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-2);
+ cursor: pointer;
+}
+
+nav li details:has(.link-list) > summary::after {
+ content: '';
+ position: static;
+ display: inline-block;
+ width: 1rem;
+ height: 1rem;
+ flex-shrink: 0;
+ margin-left: 0;
+ background-color: var(--color-text-secondary);
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='9 18 15 12 9 6'%3E%3C/polyline%3E%3C/svg%3E");
+ mask-size: contain;
+ mask-repeat: no-repeat;
+ mask-position: center;
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='9 18 15 12 9 6'%3E%3C/polyline%3E%3C/svg%3E");
+ -webkit-mask-size: contain;
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-position: center;
+ transition: transform var(--transition-base), background-color var(--transition-fast);
+}
+
+nav li details:has(.link-list) > summary:hover::after {
+ background-color: var(--color-accent-primary);
+}
+
+nav li details:has(.link-list)[open] > summary::after {
+ transform: rotate(90deg);
+ background-color: var(--color-accent-primary);
}
/* 8. Main Content (Center Column) */
@@ -1333,7 +1450,7 @@ aside.table-of-contents .toc-list > .toc-h3 {
}
body.has-toc {
- grid-template-columns: minmax(var(--layout-sidebar-width-min), var(--layout-sidebar-width-max)) 1fr;
+ grid-template-columns: var(--layout-sidebar-width) 1fr;
grid-template-areas:
"header header"
"nav main"
@@ -1343,10 +1460,6 @@ aside.table-of-contents .toc-list > .toc-h3 {
/* Tablet adjustments (between mobile and desktop) */
@media (width >= 768px) and (width <= 1023px) {
- nav {
- width: 240px;
- }
-
header.top-navbar {
padding: 0 var(--space-6);
}
diff --git a/lib/rdoc/generator/template/aliki/index.rhtml b/lib/rdoc/generator/template/aliki/index.rhtml
index b86154a33e..12ac5a2962 100644
--- a/lib/rdoc/generator/template/aliki/index.rhtml
+++ b/lib/rdoc/generator/template/aliki/index.rhtml
@@ -1,4 +1,5 @@
+<%= render '_icons.rhtml' %>
<%= render '_header.rhtml' %>
<%= render '_sidebar_toggle.rhtml' %>
diff --git a/lib/rdoc/generator/template/aliki/page.rhtml b/lib/rdoc/generator/template/aliki/page.rhtml
index f4a752fe6e..3e19f1c3e6 100644
--- a/lib/rdoc/generator/template/aliki/page.rhtml
+++ b/lib/rdoc/generator/template/aliki/page.rhtml
@@ -1,4 +1,5 @@
+<%= render '_icons.rhtml' %>
<%= render '_header.rhtml' %>
<%= render '_sidebar_toggle.rhtml' %>