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

Use CSS custom properties for component matchMedia() #4562

Merged
merged 7 commits into from
Dec 15, 2023
Merged
21 changes: 21 additions & 0 deletions packages/govuk-frontend/src/govuk/common/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,27 @@ export function getFragmentFromUrl(url) {
return url.split('#').pop()
}

/**
* Get GOV.UK Frontend breakpoint value from CSS custom property
*
* @private
* @param {string} name - Breakpoint name
* @returns {{ property: string, value?: string }} Breakpoint object
*/
export function getBreakpoint(name) {
const property = `--govuk-frontend-breakpoint-${name}`

// Get value from `<html>` with breakpoints on CSS :root
const value = window
.getComputedStyle(document.documentElement)
.getPropertyValue(property)

return {
property,
value: value || undefined
}
}

/**
* Move focus to element
*
Expand Down
40 changes: 30 additions & 10 deletions packages/govuk-frontend/src/govuk/components/header/header.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getBreakpoint } from '../../common/index.mjs'
import { ElementError } from '../../errors/index.mjs'
import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'

Expand Down Expand Up @@ -83,23 +84,42 @@ export class Header extends GOVUKFrontendComponent {
this.$menu = $menu
this.$menuButton = $menuButton

// Set the matchMedia to the govuk-frontend desktop breakpoint
this.mql = window.matchMedia('(min-width: 48.0625em)')
this.setupResponsiveChecks()

this.$menuButton.addEventListener('click', () =>
this.handleMenuButtonClick()
)
}

/**
* Setup viewport resize check
*
* @private
*/
setupResponsiveChecks() {
const breakpoint = getBreakpoint('desktop')

if (!breakpoint.value) {
throw new ElementError({
componentName: 'Header',
identifier: `CSS custom property (\`${breakpoint.property}\`) on pseudo-class \`:root\``
})
}

// Media query list for GOV.UK Frontend desktop breakpoint
this.mql = window.matchMedia(`(min-width: ${breakpoint.value})`)

// MediaQueryList.addEventListener isn't supported by Safari < 14 so we need
// to be able to fall back to the deprecated MediaQueryList.addListener
if ('addEventListener' in this.mql) {
this.mql.addEventListener('change', () => this.syncState())
this.mql.addEventListener('change', () => this.checkMode())
} else {
// @ts-expect-error Property 'addListener' does not exist
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
this.mql.addListener(() => this.syncState())
this.mql.addListener(() => this.checkMode())
}

this.syncState()
this.$menuButton.addEventListener('click', () =>
this.handleMenuButtonClick()
)
this.checkMode()
}

/**
Expand All @@ -112,7 +132,7 @@ export class Header extends GOVUKFrontendComponent {
*
* @private
*/
syncState() {
checkMode() {
if (!this.mql || !this.$menu || !this.$menuButton) {
return
}
Expand Down Expand Up @@ -142,7 +162,7 @@ export class Header extends GOVUKFrontendComponent {
*/
handleMenuButtonClick() {
this.menuIsOpen = !this.menuIsOpen
this.syncState()
this.checkMode()
}

/**
Expand Down
14 changes: 12 additions & 2 deletions packages/govuk-frontend/src/govuk/components/tabs/tabs.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getFragmentFromUrl } from '../../common/index.mjs'
import { getBreakpoint, getFragmentFromUrl } from '../../common/index.mjs'
import { ElementError } from '../../errors/index.mjs'
import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'

Expand Down Expand Up @@ -105,7 +105,17 @@ export class Tabs extends GOVUKFrontendComponent {
* @private
*/
setupResponsiveChecks() {
this.mql = window.matchMedia('(min-width: 40.0625em)')
const breakpoint = getBreakpoint('tablet')

if (!breakpoint.value) {
throw new ElementError({
componentName: 'Tabs',
identifier: `CSS custom property (\`${breakpoint.property}\`) on pseudo-class \`:root\``
})
}

// Media query list for GOV.UK Frontend tablet breakpoint
this.mql = window.matchMedia(`(min-width: ${breakpoint.value})`)

// MediaQueryList.addEventListener isn't supported by Safari < 14 so we need
// to be able to fall back to the deprecated MediaQueryList.addListener
Expand Down
2 changes: 1 addition & 1 deletion packages/govuk-frontend/src/govuk/core/_all.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import "govuk-frontend-version";
@import "govuk-frontend-properties";
@import "links";
@import "lists";
@import "typography";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@
// This variable is automatically overwritten during builds and releases.
// It doesn't need to be updated manually.
--govuk-frontend-version: "development";

// CSS custom property for each breakpoint
@each $name, $value in $govuk-breakpoints {
--govuk-frontend-breakpoint-#{$name}: #{govuk-px-to-rem($value)};
}
}
14 changes: 12 additions & 2 deletions packages/govuk-frontend/tasks/build/release.unit.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,18 @@ describe('dist/', () => {
)
})

it('should contain version number custom property', () => {
it('should contain CSS custom properties', () => {
// GOV.UK Frontend version number
expect(stylesheet).toContain(`--govuk-frontend-version:"${pkg.version}"`)

// Breakpoints for `window.matchMedia()` in Header, Tabs components
expect(stylesheet).toContain('--govuk-frontend-breakpoint-mobile:20rem')
expect(stylesheet).toContain(
'--govuk-frontend-breakpoint-tablet:40.0625rem'
)
expect(stylesheet).toContain(
'--govuk-frontend-breakpoint-desktop:48.0625rem'
)
})
})

Expand All @@ -70,7 +80,7 @@ describe('dist/', () => {
'../packages/govuk-frontend/src/govuk/all.scss'
)
expect(sourcemap.sources).toContain(
'../packages/govuk-frontend/src/govuk/core/_govuk-frontend-version.scss'
'../packages/govuk-frontend/src/govuk/core/_govuk-frontend-properties.scss'
)
})
})
Expand Down