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

AppFrame component #2147

Merged
merged 5 commits into from Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
142 changes: 142 additions & 0 deletions 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 (
<>
<div className={clsx('AppFrame')}>

<div className={clsx('AppFrame-a11yNav')}>
{a11yNavItems.map(link => (
<a className={clsx('AppFrame-a11yLink')} href={link.url}>{link.label}</a>
))}
</div>

<div className={clsx('AppFrame-main')}>

<div className={clsx('AppFrame-header-wrapper')}>

<div className={clsx('AppFrame-header')}>
{headerChildren}
</div>

<div id="start-of-content"></div>

{subheaderChildren && (
<div className={clsx('AppFrame-subheader')}>
{subheaderChildren}
</div>
)}

</div>
<div className={clsx('AppFrame-body')}>
{bodyChildren}
</div>
</div>
<div className={clsx('AppFrame-footer')}>
{footerChildren}
</div>
</div>

{_debug && (
<>
<style type="text/css">{`
.AppFrame {

}
.AppFrame-header,
.AppFrame-subheader,
.AppFrame-body,
.AppFrame-footer {
padding: 16px;
}
.AppFrame-header {
background: pink;
}
.AppFrame-subheader {
background: lightblue;
}
.AppFrame-footer {
background: pink;
}
`}</style>
</>
)}
</>
);
};

export const Playground = AppFrameTemplate.bind({})

Playground.args = {
_debug: true,
headerChildren: "Header slot",
subheaderChildren: "Subheader slot",
bodyChildren: "Body slot",
footerChildren: "Footer slot",
};
155 changes: 155 additions & 0 deletions 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);
vdepizzol marked this conversation as resolved.
Show resolved Hide resolved
pointer-events: none;
background: var(--color-border-default);
border-radius: 50%;
vdepizzol marked this conversation as resolved.
Show resolved Hide resolved
}

&: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) {
vdepizzol marked this conversation as resolved.
Show resolved Hide resolved
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;
}
}
1 change: 1 addition & 0 deletions 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';
Expand Down