Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add anchor links to headings for easier sharing #2481

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
10 changes: 10 additions & 0 deletions lib/file-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ exports.getNunjucksCode = path => {
)
}

// Renders a Nunjucks macro component outside of a template.
exports.renderNunjucksMacro = (macroName, macroPath, options) => {
const macroOptions = JSON.stringify(options, undefined, 2)

let macroString = `{%- from "${macroPath}" import ${macroName} -%}`
macroString += `{{- ${macroName}(${macroOptions}) -}}`

return nunjucks.renderString(macroString)
}

// This helper function takes a path of a *.md.njk file and
// returns the frontmatter as an object
exports.getFrontmatter = path => {
Expand Down
12 changes: 12 additions & 0 deletions lib/marked-renderer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const { marked } = require('marked')

const { renderNunjucksMacro } = require('./file-helper.js')

/**
* Custom markdown renderer
*/
Expand All @@ -26,6 +28,16 @@ class DesignSystemRenderer extends marked.Renderer {
.replace('<code', '<code tabindex="0"')
}

/**
* Render headings as anchor headings
*/
heading (text, level) {
return renderNunjucksMacro('heading', '_heading.njk', {
text,
level
})
}

/**
* Avoid wrapping `<img>` image tags in `<p>` paragraphs
*/
Expand Down
6 changes: 6 additions & 0 deletions src/images/icons.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion src/stylesheets/components/_example.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// Example styles
.app-example-wrapper {
@include govuk-responsive-margin(6, "top");
@include govuk-responsive-margin(6, "bottom");
@include govuk-font(19);
}

.app-example-wrapper + * {
margin-top: 0;
@include govuk-responsive-padding(6, "top");
}

.app-example {
Expand Down
41 changes: 41 additions & 0 deletions src/stylesheets/components/_heading.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
$app-heading-icon-size: .6em;
$app-heading-icon-gutter: .15em;

@include govuk-exports("app-heading") {
// Anchored headings
.app-heading {
position: relative;
left: -($app-heading-icon-size + $app-heading-icon-gutter);
padding-left: $app-heading-icon-size + $app-heading-icon-gutter;
}

.app-heading__icon {
margin-left: -($app-heading-icon-size + $app-heading-icon-gutter);
padding-right: $app-heading-icon-gutter;
}

.app-heading__anchor:link,
.app-heading__anchor:visited {
color: inherit;
}

.app-heading__anchor:not(:hover):not(:active) {
text-decoration: none;
}


// Only show icon with pointers that have hover (desktop)
// Prevents mobile from accidentally having icons appear when pressing headings.
@media (hover: hover) {
.app-heading__anchor:where(:hover, :focus) .app-heading__icon {
opacity: 1;
}
}

.app-heading__icon {
width: $app-heading-icon-size;
height: $app-heading-icon-size;
opacity: 0;
fill: govuk-colour("dark-grey");
}
}
3 changes: 2 additions & 1 deletion src/stylesheets/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ $app-code-color: #d13118;
@import "components/example";
@import "components/footer";
@import "components/header";
@import "components/heading";
@import "components/highlight";
@import "components/image-card";
@import "components/masthead";
Expand Down Expand Up @@ -325,7 +326,7 @@ pre .language-plaintext {
@extend %govuk-list--bullet;
}

a {
a:not([class]) {
@extend %govuk-link;

&:focus code {
Expand Down
2 changes: 1 addition & 1 deletion views/partials/_example.njk
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
{% if display %}
<div class="app-example {{ "app-example--tabs" if params.html or params.nunjucks }}">
<div class="app-example__toolbar">
<a href="{{ exampleURL }}" class="app-example__new-window" target="_blank">
<a href="{{ exampleURL }}" class="govuk-link app-example__new-window" target="_blank">
Open this
{#- Don't use full title visually as the context is shown based on location of this link #}
<span class="govuk-visually-hidden">{{ exampleTitle | lower }}</span>
Expand Down
10 changes: 10 additions & 0 deletions views/partials/_heading.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{%- macro heading(params) -%}
<h{{ params.level }} class="app-heading{% if params.classes %} {{ params.classes }}{% endif %}" id="{{ params.text | slugger }}">
<a class="app-heading__anchor govuk-link" href="#{{ params.text | slugger }}">
<svg height="25" width="25" viewBox="0 0 512 512" class="app-heading__icon" aria-hidden="true" focusable="false">
<use xlink:href="/images/icons.svg#link" />
</svg>
{{- params.text -}}
</a>
</h{{- params.level -}}>
{%- endmacro -%}