Skip to content

Commit

Permalink
Merge pull request #132 from w3c/feature/#469-heading-anchors
Browse files Browse the repository at this point in the history
Feature/#469 heading anchors
  • Loading branch information
jean-gui authored Oct 15, 2024
2 parents db5cb50 + 2574196 commit 55bd03a
Show file tree
Hide file tree
Showing 50 changed files with 1,901 additions and 1,567 deletions.
2 changes: 2 additions & 0 deletions assets-src/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {formErrorSummary} from "./main/form-error-summary";
import {navigation} from "./main/navigation";
import {responsiveTables} from "./main/responsive-tables";
import {flashes} from "./main/flashes";
import {headingAnchors} from './main/heading-anchors';

function domLoadedActions() {
accountMenu();
Expand All @@ -15,6 +16,7 @@ function domLoadedActions() {
formErrorSummary();
responsiveTables();
flashes();
headingAnchors();

/* Create a navDoubleLevel object and initiate double-level navigation for a <ul> with the correct data-component attribute */
const navDoubleIntro = document.querySelector('ul[data-component="nav-double-intro"]');
Expand Down
91 changes: 91 additions & 0 deletions assets-src/js/main/heading-anchors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Add anchor links to any H2 - H6 within the <main> element that:
* - are not children of a <nav> element
* - do not have an ancestor with the data-anchor="no" attribute, with
* the `hidden` attribute or with the .visuallyhidden class
* - do not themselves have the data-anchor="no" attribute, the `hidden`
* attribute or the .visuallyhidden class
*
* If a heading does not already possess an ID, use regular expressions on
* the textContent of the heading to generate a string that is valid to
* use for both the heading ID and the anchor href. Supports non-Latin
* scripts by matching any Unicode letter - \p{L} - or number - \p{N}. The
* u flag enables Unicode matching, to support characters from any script.
*
* Otherwise, generate the anchor using the existing heading ID value.
*/

import {translate} from './translations';

let headingAnchors = function () {

let languageCode = document.documentElement.lang;

// Only add heading anchor links on "full" sites
if (languageCode === 'en' || languageCode === 'ja' || languageCode === 'zh-hans') {

let headingsArray= Array.from(document.querySelectorAll('main h2, main h3, main h4, main h5, main h6'));

if (headingsArray.length > 0) {

// Filter out headings that:
// - Are not children of <nav>
// - Do not have an ancestor with the data-anchor="no" attribute
// - Do not have an ancestor with the `hidden` attribute
// - Do not have an ancestor with the `.visuallyhidden` class
// - Do not themselves have the data-anchor="no" attribute
// - Do not themselves have the `hidden` attribute
// - Do not themselves have the `.visuallyhidden` class
let targetedHeadings = headingsArray.filter(function(heading) {
let insideNav = heading.closest('nav') !== null;
let parentHasDataAttribute = heading.closest('[data-anchor="no"]') !== null;
let hiddenParent = heading.closest('[hidden]') !== null;
let visuallyhiddenParent = heading.closest('.visuallyhidden') !== null;
let hasDataAttribute = heading.getAttribute('data-anchor') === 'no';
let isHidden = heading.getAttribute('hidden');
let isVisuallyhidden = heading.classList.contains('visuallyhidden');

return !insideNav && !parentHasDataAttribute && !hiddenParent && !visuallyhiddenParent && !hasDataAttribute && !isHidden && !isVisuallyhidden;
});

if (targetedHeadings.length > 0) {
targetedHeadings.forEach(function(heading) {

let anchor = document.createElement('a');
let anchorHref;

// If the heading already has an ID, use this for constructing the anchor
if (heading.getAttribute('id')) {
anchorHref = heading.id;
} else {
// If the heading does not already have an ID, generate anchor href from the heading text. Steps are:
// - Remove leading/trailing spaces
// - Use RegEx to remove invalid characters but keep all Unicode letters/numbers
// - Use RegEx to replace spaces with hyphens
// - convert to lowercase as per URL policy
anchorHref = heading.textContent
.trim()
.replace(/[^\p{L}\p{N}\s-]/gu, '')
.replace(/\s+/g, '-')
.toLowerCase();
heading.id = anchorHref;
}

anchor.setAttribute('href', '#' + anchorHref);
anchor.setAttribute('class', 'heading-anchor');
anchor.innerHTML = '<span aria-hidden="true">&sect;</span>'
+'<span class="visuallyhidden">'
+ translate.translate('anchor', languageCode) + '</span>';
heading.textContent += '\xa0';
heading.appendChild(anchor);

});
}

}

}

}

export {headingAnchors};
3 changes: 3 additions & 0 deletions assets-src/js/main/translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const translate = {
},
'en': {
'admin': 'Admin',
'anchor': 'anchor',
'backToMainMenu': 'Back to main menu',
'cancelReply': 'Cancel reply',
'controlsDescription': 'carousel controls',
Expand Down Expand Up @@ -116,6 +117,7 @@ const translate = {
},
'ja': {
'admin': 'アドミン',
'anchor': '__anchor',
'backToMainMenu': 'メインメニューに戻る',
'cancelReply': '返信をキャンセル',
'controlsDescription': 'コントロールの説明',
Expand Down Expand Up @@ -154,6 +156,7 @@ const translate = {
},
'zh-hans': {
'admin': '管理',
'anchor': '__anchor',
'backToMainMenu': '返回主目录',
'cancelReply': '取消回复',
'controlsDescription': '轮播图控件',
Expand Down
26 changes: 24 additions & 2 deletions assets-src/styles/sass/30-base/_links.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ a.with-icon--after {
border: 0;
color: $link-color;
cursor: pointer;
padding-left: rem(2);
padding-right: rem(2);
padding-inline: rem(2);
text-decoration: underline; /* 1 */
text-decoration-skip-ink: auto; // Not supported by Safari
text-underline-offset: 0.25em; // Supported by Safari
Expand Down Expand Up @@ -48,4 +47,27 @@ a.with-icon--after {
color: $black;
outline-width: 0; /* 2 */
}
}

.heading-anchor {
@extend a, :not([class]);

color: $storm-gray;
font-weight: normal;
opacity: 0.82;
text-underline-offset: auto;

&:visited {
color: $storm-gray;
}

&:hover {
color: $storm-gray;
opacity: 1;
}

&:focus {
color: $black;
opacity: 1;
}
}
1 change: 1 addition & 0 deletions assets-src/styles/sass/30-base/_print.scss
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,7 @@ footer {
.banner,
#lang-nav,
.logo-link .visuallyhidden,
.heading-anchor,
[data-trigger],
[data-toggle],
#global-nav ul,
Expand Down
2 changes: 1 addition & 1 deletion design-system-config.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
'Styles' => '/styles/',
'Layouts' => '/layouts/',
'Components' => '/components/',
'Third party plugins' => '/third-party-plugins/',
'Scripts' => '/scripts/',
'Templates' => '/templates/',
],
'templates_path' => 'design-system-templates',
Expand Down
2 changes: 1 addition & 1 deletion design-system-templates/base.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
{% block breadcrumbs %}
{% endblock %}

<main id="main">
<main id="main" {% block extra_main_attributes %}{% endblock %}>
{% block content %}
{% endblock %}
</main>
Expand Down
4 changes: 2 additions & 2 deletions design-system-templates/components/activity.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
</div>
</li>
<li>
<div class="card card--event workshop" data-component="card">
<div class="card card--event workshop" data-component="card" data-anchor="no">
<div class="card__text">
<h3 class="card__heading"><a href="path/to/page" class="card__link">How do you want to pay?</a></h3>
<p class="txt-pluto with-icon--before"><img class="icon" src="/dist/assets/svg/calendar.svg" width="15" height="15" alt aria-hidden="true" /> <time datetime="2020-11-15">15 November 2020</time></p>
Expand All @@ -46,7 +46,7 @@
</div>
</li>
<li>
<div class="card card--event talk" data-component="card">
<div class="card card--event talk" data-component="card" data-anchor="no">
<div class="card__text">
<h3 class="card__heading"><a href="path/to/page" class="card__link">Cognitive AI - mimicking how we think</a></h3>
<p class="txt-pluto with-icon--before"><img class="icon" src="/dist/assets/svg/calendar.svg" width="15" height="15" alt aria-hidden="true" /> <time datetime="2020-12-20">20 December 2020</time></p>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="event-list">
<div class="card card--event meeting" data-component="card">
<div class="card card--event meeting" data-component="card" data-anchor="no">
<div class="card__text">
<div class="l-sidebar">
<div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="component--collapsibles" data-component="collapsibles">
<div class="component--collapsibles" data-component="collapsibles" data-anchor="no">
<div>
<h3 data-heading="collapsibles">AI KR (Artificial Intelligence Knowledge Representation) <span>Community group</span></h3>
<div>
Expand Down
2 changes: 1 addition & 1 deletion design-system-templates/components/evangelists.html.twig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="component component--evangelists">
<div class="component component--evangelists" data-anchor="no">
<div class="component--evangelists__text">
<h2 class="txt-earth">Need more information tailored to your organisation?</h2>
<p>Our Evangelists understand that every organisation is different! They represent W3C in various locations and are an extension of W3C's Business Development Team. They are responsible for identifying and recruiting new W3C Members, running local events, promoting W3C Training and fostering Sponsorship.</p>
Expand Down
2 changes: 1 addition & 1 deletion design-system-templates/components/group-list.html.twig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="u-full-width component component--group-list">
<div class="u-full-width component component--group-list" data-anchor="no">
<div class="l-center">
<div class="component--group-list__intro">
<h2>Active groups</h2>
Expand Down
8 changes: 4 additions & 4 deletions design-system-templates/components/listings-events.html.twig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="event-list">

<article class="card card--event meeting" data-component="card" aria-labelledby="event-1-date event-1-title">
<article class="card card--event meeting" data-component="card" data-anchor="no" aria-labelledby="event-1-date event-1-title">
<div class="card__text">
<div class="l-sidebar">
<div>
Expand All @@ -26,7 +26,7 @@
</div>
</article>

<article class="card card--event workshop" data-component="card" aria-labelledby="event-2-date event-2-title">
<article class="card card--event workshop" data-component="card" data-anchor="no" aria-labelledby="event-2-date event-2-title">
<div class="card__text">
<div class="l-sidebar">
<div>
Expand Down Expand Up @@ -57,7 +57,7 @@
</div>
</article>

<article class="card card--event talk" data-component="card" aria-labelledby="event-3-date event-3-title">
<article class="card card--event talk" data-component="card" data-anchor="no" aria-labelledby="event-3-date event-3-title">
<div class="card__text">
<div class="l-sidebar">
<div>
Expand Down Expand Up @@ -88,7 +88,7 @@
</div>
</article>

<article class="l-sidebar card card--event conference" data-component="card" aria-labelledby="event-4-date event-4-title">
<article class="l-sidebar card card--event conference" data-component="card" data-anchor="no" aria-labelledby="event-4-date event-4-title">
<div>
<div class="not-sidebar card__text">
<div class="l-sidebar">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="people-list">
<div class="people-list" data-anchor="no">

<div class="l-sidebar card--user">
<div>
Expand Down
2 changes: 1 addition & 1 deletion design-system-templates/components/members.html.twig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="component component--members u-full-width">
<div class="component component--members u-full-width" data-anchor="no">
<div class="l-center">
<h2 class="visuallyhidden">W3C Members</h2>
<p>20 member organizations are involved in the web payment ecosystem, including:</p>
Expand Down
16 changes: 8 additions & 8 deletions design-system-templates/components/notes.html.twig
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<div class="l-box note note--info" role="status">
<h2 class="txt-saturn">Informative note heading</h2>
<div class="l-box note note--info" role="status" aria-labelledby="unique-id-1" tabindex="-1" data-anchor="no">
<h2 id="unique-id-1" class="txt-saturn">Informative note heading</h2>
<p>Group calendering is currently in beta-test. We encourage everyone to use it and report feedback and issues to its <a href="#1">dedicated GitHub repository</a>.</p>
</div>

<div class="l-box note note--success" role="status">
<h2 class="txt-saturn">Success note heading</h2>
<div class="l-box note note--success" role="status" aria-labelledby="unique-id-2" tabindex="-1" data-anchor="no">
<h2 id="unique-id-2" class="txt-saturn">Success note heading</h2>
<p>Main profile successfully updated.</p>
</div>

<div class="l-box note note--warning" role="status">
<h2 class="txt-saturn">Warning note heading</h2>
<div class="l-box note note--warning" role="status" aria-labelledby="unique-id-3" tabindex="-1" data-anchor="no">
<h2 id="unique-id-3" class="txt-saturn">Warning note heading</h2>
<p>We could not detect your affiliation based on your email address. Please select your affiliation below or <a href="#2">go back to use your corporate email address.</a></p>
</div>

<div class="l-box note note--error" role="alert">
<h2 class="txt-saturn">Error note heading</h2>
<div class="l-box note note--error" role="alert" aria-labelledby="unique-id-4" tabindex="-1" data-anchor="no">
<h2 id="unique-id-4" class="txt-saturn">Error note heading</h2>
<p>There has been an error.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<p>Copyright © 2023 W3C - generated {{ date|date("d M Y, H:i:s e") }}</p>
<p>Copyright © {{ date|date("Y") }} W3C - generated {{ date|date("d M Y, H:i:s e") }}</p>
<p>Feedback is welcome and can be submitted as a <a href="https://github.com/w3c/w3c-website/issues">GitHub issue</a>.</p>
10 changes: 5 additions & 5 deletions design-system-templates/example-pages/default.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@

</article>

<section class="related">
<div class="related__inner">
<h2 class="txt-jupiter">Related content</h2>
<div class="related">
<nav class="related__inner" aria-labelledby="related-label">
<h2 id="related-label" class="txt-jupiter">Related content</h2>
<ul class="clean-list" role="list">
<li><a href="path/to/page">Excepteur sint occaeca</a></li>
<li><a href="path/to/page">Excepteur sint occaeca</a></li>
Expand All @@ -40,8 +40,8 @@
<li><a href="path/to/page">Excepteur sint occaeca</a></li>
<li><a href="path/to/page">Excepteur sint occaeca</a></li>
</ul>
</div>
</section>
</nav>
</div>
</div>
{% endblock %}

Expand Down
2 changes: 2 additions & 0 deletions design-system-templates/example-pages/home.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

{% block body_class %}class="home"{% endblock %}

{% block extra_main_attributes%}data-anchor="no"{% endblock %}

{% block content %}

{% include 'components/hero-home.html.twig' %}
Expand Down
10 changes: 5 additions & 5 deletions design-system-templates/example-pages/post.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@

</article>

<section class="related">
<div class="related__inner">
<h2 class="txt-jupiter">Related to this post</h2>
<div class="related">
<nav class="related__inner" aria-labelledby="related-label">
<h2 id="related-label" class="txt-jupiter">Related to this post</h2>
<div>
<h3>Tags</h3>
<ul class="clean-list" role="list">
Expand Down Expand Up @@ -79,8 +79,8 @@
<li><a href="path/to/page">Specification 2</a></li>
</ul>
</div>
</div>
</section>
</nav>
</div>

</div>
{% include 'components/rss.html.twig' %}
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ More generic and wide-reaching styles sit within the lower numbered levels, with
- **40-layouts:**<br> heavily influenced by [Every Layout](https://every-layout.dev/), these are the styles for the basic layout types, which can be combined and customised to make a variety of components and templates - [more about layouts](layouts/README.md)
- **50-core-components:**<br> the basic components available for use, un-enhanced by JavaScript - [more about components](components/README.md)
- **60-advanced-components:**<br> components that are enhanced in some way with JavaScript - [more about components](components/README.md)
- **70-third-party-plugins:**<br> any functionality that comes from external sources - [more about third-party plugins](third-party-plugins/README.md)
- **70-third-party-plugins:**<br> styles for components/functionality from sources external to the design system, typically via scripts. They may include some customisations specifically required to fit in with the design system.
- **80-templates:**<br> styles required for specific page templates and/or content types - [more about templates](templates/README.md)
- **90-utilities:**<br> overrides or helper classes - [more about utilities](styles/utilities.md)

Expand Down
5 changes: 5 additions & 0 deletions docs/scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Scripts

Documenting functionality that is added via scripts.

For sources that are external to the design system, there may be some customisations specifically required.
Loading

0 comments on commit 55bd03a

Please sign in to comment.