From 9dd2a49e2633f337c2316a43907f17dfd5c3173b Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Fri, 1 Jul 2022 13:00:14 -0700 Subject: [PATCH] AppFrame component (#2147) * Add AppFrame component * Please stylelint * Update src/layout/app-frame.scss Co-authored-by: Katie Langerman * Use tokens * Add changeset Co-authored-by: Katie Langerman --- .changeset/lovely-rivers-bow.md | 5 + .../components/Layout/AppFrame.stories.jsx | 142 ++++++++++++++++ src/layout/app-frame.scss | 155 ++++++++++++++++++ src/layout/index.scss | 1 + 4 files changed, 303 insertions(+) create mode 100644 .changeset/lovely-rivers-bow.md create mode 100644 docs/src/stories/components/Layout/AppFrame.stories.jsx create mode 100644 src/layout/app-frame.scss diff --git a/.changeset/lovely-rivers-bow.md b/.changeset/lovely-rivers-bow.md new file mode 100644 index 0000000000..d64d397050 --- /dev/null +++ b/.changeset/lovely-rivers-bow.md @@ -0,0 +1,5 @@ +--- +"@primer/css": minor +--- + +Add AppFrame component diff --git a/docs/src/stories/components/Layout/AppFrame.stories.jsx b/docs/src/stories/components/Layout/AppFrame.stories.jsx new file mode 100644 index 0000000000..7542c26dfa --- /dev/null +++ b/docs/src/stories/components/Layout/AppFrame.stories.jsx @@ -0,0 +1,142 @@ +import React from 'react' +import clsx from 'clsx' +//import { AppHeaderTemplate as AppHeader } from '../App/AppHeader.stories' + +export default { + title: 'Components/Layout/AppFrame', + parameters: { + layout: 'fullscreen' + }, + + excludeStories: ['AppFrameTemplate'], + argTypes: { + + // Debug + + _debug: { + control: { + type: 'boolean', + } + }, + + a11yNavItems: { + type: 'array', + }, + + // Children + + headerChildren: { + description: 'creates a slot for header children', + table: { + category: 'HTML' + } + }, + subheaderChildren: { + description: 'creates a slot for subheader children', + table: { + category: 'HTML' + } + }, + bodyChildren: { + description: 'creates a slot for body children', + table: { + category: 'HTML' + } + }, + footerChildren: { + description: 'creates a slot for footer children', + table: { + category: 'HTML' + } + }, + }, +} + +export const AppFrameTemplate = ({ + _debug, + a11yNavItems, + headerChildren, + subheaderChildren, + bodyChildren, + footerChildren, +}) => { + + // Default values + a11yNavItems = a11yNavItems ?? [ + {url: '#start-of-content', label: 'Skip to content'}, + {url: '/', label: 'GitHub homepage'}, + ]; + + return ( + <> +
+ +
+ {a11yNavItems.map(link => ( + {link.label} + ))} +
+ +
+ +
+ +
+ {headerChildren} +
+ +
+ + {subheaderChildren && ( +
+ {subheaderChildren} +
+ )} + +
+
+ {bodyChildren} +
+
+
+ {footerChildren} +
+
+ + {_debug && ( + <> + + + )} + + ); +}; + +export const Playground = AppFrameTemplate.bind({}) + +Playground.args = { + _debug: true, + headerChildren: "Header slot", + subheaderChildren: "Subheader slot", + bodyChildren: "Body slot", + footerChildren: "Footer slot", +}; \ No newline at end of file diff --git a/src/layout/app-frame.scss b/src/layout/app-frame.scss new file mode 100644 index 0000000000..133d1f1ccb --- /dev/null +++ b/src/layout/app-frame.scss @@ -0,0 +1,155 @@ +// stylelint-disable max-nesting-depth +// stylelint-disable primer/spacing +// stylelint-disable primer/borders + +.AppFrame { + + // AppFrame structure + // =================== + // + // .AppFrame + // ├─ .AppFrame-a11yNav + // │ ├─ .AppFrame-a11yLink + // │ + // ├─ .AppFrame-main + // │ ├─ .AppFrame-header-wrapper + // │ │ ├─ .AppFrame-header + // │ │ ├─ .AppFrame-subheader + // │ │ + // │ ├─ #start-of-content + // │ ├─ .AppFrame-body + // │ ├─ .AppFrame-footer + + // Accessibility navigation + + .AppFrame-a11yNav { + position: absolute; + z-index: 1000; + display: flex; + width: 100%; + padding: var(--base-size-16, 16px); + background: var(--color-canvas-inset); + padding-block-end: calc(var(--base-size-16, 16px) - var(--primer-borderWidth-thin, 1px)); + isolation: isolate; + align-items: center; + gap: var(--base-size-8, 8px); + + &:not(:focus-within) { + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(1px, 1px, 1px, 1px); + border: 0; + } + + &:focus-within { + top: 0; + left: 0; + + // Narrow viewport + @media (max-width: #{map-get($breakpoints, 'md') - 0.02px}) { + justify-content: center; + } + } + } + + .AppFrame-a11yLink { + transition: none; + + &:not(:focus) { + display: block; + width: var(--base-size-8, 8px); + height: var(--base-size-8, 8px); + overflow: hidden; + text-indent: var(--base-size-128, 128px); + pointer-events: none; + background: var(--color-border-default); + border-radius: var(--primer-borderRadius-full, 100vh); + } + + &:focus { + z-index: 20; + display: grid; + width: auto; + height: auto; + min-height: var(--primer-control-medium-size, 32px); + padding: 0 var(--primer-control-medium-paddingInline-spacious, 16px); + overflow: auto; + color: var(--color-fg-on-emphasis); + background: var(--color-accent-emphasis); + border-radius: var(--primer-borderRadius-full, 100vh); + align-items: center; + + @media (pointer: coarse) { + &::after { + @include minTouchTarget(var(--primer-control-minTarget-coarse, 44px)); + } + } + + @media (prefers-reduced-motion: no-preference) { + animation: AppFrame-a11yLink-focus 200ms ease-out; + } + + @keyframes AppFrame-a11yLink-focus { + 0% { + color: var(--color-accent-emphasis); + transform: scale(0.3, 0.25); + } + + 50% { + color: var(--color-accent-emphasis); + transform: scale(1, 1); + } + + 55% { + color: var(--color-fg-on-emphasis); + } + + 100% { + transform: scaleX(1); + } + } + } + } + + .AppFrame-main { + display: flex; + min-height: 100vh; + flex-direction: column; + + @supports (height: 100dvh) { + min-height: 100dvh; + } + } + + .AppFrame-header-wrapper { + position: relative; + height: min-content; + overflow: visible; + + .AppFrame-header { + position: sticky; + top: 0; + z-index: 1; + } + } + + .AppFrame-header { + flex: 0 0 auto; + } + + .AppFrame-subheader { + flex: 0 0 auto; + } + + .AppFrame-body { + flex: 1 0; + height: 100%; + } + + .AppFrame-footer { + flex: 0 0 auto; + } +} diff --git a/src/layout/index.scss b/src/layout/index.scss index 9773f3bae9..88c26c43e0 100644 --- a/src/layout/index.scss +++ b/src/layout/index.scss @@ -1,4 +1,5 @@ @import '../support/index.scss'; +@import './app-frame.scss'; @import './mixins.scss'; @import './container.scss'; @import './grid.scss';