Skip to content

feat(appframe, appframe side nav): prototype new components #4002

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

Draft
wants to merge 28 commits into
base: spectrum-two
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e6856bf
feat(appframe): beginning of new component layout and styles
jawinn Dec 9, 2024
23fe58e
feat(appframesidenav): initial setup of new component
jawinn Dec 9, 2024
95690e7
feat(appframe): use app frame side nav template
jawinn Dec 13, 2024
1f6e03b
feat(appframesidenav): rename condensed and include max-width
jawinn Dec 13, 2024
e757492
feat(appframe): header example and show hide menu on click
jawinn Dec 13, 2024
41e5ce9
feat(appframesidenav): use corner rounding token
jawinn Dec 13, 2024
9641297
feat(appframesidenav): set current item on click and add down state
jawinn Dec 16, 2024
a1769c6
feat(appframe): rounded content area option
jawinn Dec 16, 2024
66d2b8c
chore(appframe): additional comments and cleanup
jawinn Dec 16, 2024
41d2dea
chore(appframe): generated mods file and rename header variable
jawinn Dec 17, 2024
8822685
fix(appframesidenav): fix control and large scale condensed sizing
jawinn Dec 17, 2024
1197e76
chore(appframe,appframesidenav): remove theme files and metadata
marissahuysentruyt Jun 30, 2025
097302e
chore(appframe,appframesidenav): add test files
marissahuysentruyt Jun 30, 2025
0f8c8d4
chore(appframesidenav): update peerDependenciesMeta
marissahuysentruyt Jun 30, 2025
9769f07
chore(appframe,appframesidenav): clean up package.json and project.json
marissahuysentruyt Jun 30, 2025
419b11d
docs(appframe,appframesidenav): update component names
marissahuysentruyt Jun 30, 2025
723986e
docs(appframe,appframesidenav): refactor items with fallbacks
marissahuysentruyt Jun 30, 2025
48b1a2c
chore(appframe,appframesidenav): update metadata
marissahuysentruyt Jun 30, 2025
0ec34a8
feat(tokens): add app frame side nav tokens
marissahuysentruyt Jul 1, 2025
b983a32
chore(appframesidenav): import useArgs hook and action button
marissahuysentruyt Jul 1, 2025
7ba4f30
docs(appframesidenav): update end section story
marissahuysentruyt Jul 1, 2025
da1add6
feat(appframesidenav): new styles
marissahuysentruyt Jul 1, 2025
9cbd45d
chore(appframesidenav): update metadata
marissahuysentruyt Jul 1, 2025
4635f80
docs(appframesidenav): add divider support and custom content
marissahuysentruyt Jul 1, 2025
ec35dbd
docs(appframesidenav): add showDividers and customContent to template
marissahuysentruyt Jul 1, 2025
fd25778
feat(appframesidenav): add styles for dividers and nested items
marissahuysentruyt Jul 3, 2025
387e5f9
doc(appframesidenav): add template support for nested nav items
marissahuysentruyt Jul 3, 2025
f50c13e
chore(appframesidenav): update metadata
marissahuysentruyt Jul 3, 2025
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
7 changes: 7 additions & 0 deletions components/appframe/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# @spectrum-css/appframe

> The Spectrum CSS app frame component

This package is part of the [Spectrum CSS project](https://github.com/adobe/spectrum-css).

See the [Spectrum CSS documentation](https://opensource.adobe.com/spectrum-css/appframe).
67 changes: 67 additions & 0 deletions components/appframe/dist/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"sourceFile": "index.css",
"selectors": [
".spectrum--dark .spectrum-AppFrame",
".spectrum-AppFrame",
".spectrum-AppFrame--noSideNav",
".spectrum-AppFrame-content",
".spectrum-AppFrame-content .spectrum-AppFrame-section:last-of-type",
".spectrum-AppFrame-content > main",
".spectrum-AppFrame-content--rounded",
".spectrum-AppFrame-content--rounded .spectrum-AppFrame-section:last-of-type",
".spectrum-AppFrame-content-intro",
".spectrum-AppFrame-header",
".spectrum-AppFrame-section",
".spectrum-AppFrame-section + .spectrum-AppFrame-section",
".spectrum-AppFrame-section .spectrum-AppFrame-section-heading",
".spectrum-AppFrame-section-grid",
".spectrum-AppFrame-section-grid > .spectrum-AppFrame-section-grid-item--wide",
".spectrum-AppFrame-section-grid:is(ul)",
".spectrum-AppFrame-section-inner > .spectrum-Body",
".spectrum-AppFrame-section-inner > .spectrum-Heading",
".spectrum-AppFrame-section-inner:not(:last-child)",
".spectrum-AppFrame-side-nav"
],
"modifiers": [
"--mod-app-frame-background",
"--mod-app-frame-content-background",
"--mod-app-frame-content-corner-radius",
"--mod-app-frame-header-height",
"--mod-app-frame-intro-background",
"--mod-app-frame-max-readable-characters",
"--mod-app-frame-section-grid-gap",
"--mod-app-frame-section-inner-area-vertical-gap",
"--mod-app-frame-section-padding"
],
"component": [
"--spectrum-app-frame-background",
"--spectrum-app-frame-content-background",
"--spectrum-app-frame-content-corner-radius",
"--spectrum-app-frame-end-side-column-inline-size",
"--spectrum-app-frame-header-height",
"--spectrum-app-frame-intro-background",
"--spectrum-app-frame-max-readable-characters",
"--spectrum-app-frame-section-gap",
"--spectrum-app-frame-section-grid-gap",
"--spectrum-app-frame-section-heading-margin-block",
"--spectrum-app-frame-section-inner-area-vertical-gap",
"--spectrum-app-frame-section-padding",
"--spectrum-app-frame-start-side-column-inline-size",
"--spectrum-app-frame-to-edge"
],
"global": [
"--spectrum-background-base-color",
"--spectrum-background-layer-1-color",
"--spectrum-component-height-400",
"--spectrum-corner-radius-extra-large-default",
"--spectrum-spacing-200",
"--spectrum-spacing-300",
"--spectrum-spacing-400",
"--spectrum-spacing-600"
],
"passthroughs": [],
"high-contrast": [
"--highcontrast-app-frame-background",
"--highcontrast-app-frame-content-background"
]
}
165 changes: 165 additions & 0 deletions components/appframe/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*!
Copyright 2023 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/

.spectrum-AppFrame {
--spectrum-app-frame-background: var(--spectrum-background-layer-1-color);
--spectrum-app-frame-content-background: var(--spectrum-background-base-color);
--spectrum-app-frame-intro-background: linear-gradient(93.59deg, #ffeccf 0%, #ffa213 63%, #ff709f 100%);

--spectrum-app-frame-content-corner-radius: var(--spectrum-corner-radius-extra-large-default);

--spectrum-app-frame-section-gap: var(--spectrum-spacing-200);
--spectrum-app-frame-to-edge: var(--spectrum-spacing-200);
--spectrum-app-frame-section-padding: var(--spectrum-spacing-400) var(--spectrum-spacing-600) var(--spectrum-spacing-600);

--spectrum-app-frame-header-height: var(--spectrum-component-height-400);

--spectrum-app-frame-start-side-column-inline-size: minmax(var(--spectrum-component-height-400), max-content);
--spectrum-app-frame-end-side-column-inline-size: var(--spectrum-app-frame-to-edge);

--spectrum-app-frame-section-grid-gap: var(--spectrum-spacing-300);
--spectrum-app-frame-section-heading-margin-block: 0px var(--spectrum-spacing-400);
--spectrum-app-frame-section-inner-area-vertical-gap: var(--spectrum-spacing-600);

/* To-do: not sure about the best way to include this limit and where to apply. */
--spectrum-app-frame-max-readable-characters: 80ch;

/* To-do: For demo purposes only; would not convert to SWC. They would need a way of providing different gradients for light/dark. */
.spectrum--dark & {
--spectrum-app-frame-intro-background: linear-gradient(94.13deg, #003041 0%, #03638c 100%);
}
}

.spectrum-AppFrame {
background: var(--highcontrast-app-frame-background, var(--mod-app-frame-background, var(--spectrum-app-frame-background)));
inline-size: 100%;
block-size: 100vh;
position: relative;
display: grid;
grid-template-areas:
"head head head"
"side main endside";
grid-template-rows: var(--mod-app-frame-header-height, var(--spectrum-app-frame-header-height)) 1fr;
grid-template-columns: var(--spectrum-app-frame-start-side-column-inline-size) 1fr var(--spectrum-app-frame-end-side-column-inline-size);
}

.spectrum-AppFrame--noSideNav {
grid-template-columns: var(--spectrum-app-frame-to-edge) 1fr var(--spectrum-app-frame-to-edge);
}

.spectrum-AppFrame-header {
grid-area: head;
}

.spectrum-AppFrame-side-nav {
grid-area: side;
}

/* Main scrollable content area */
.spectrum-AppFrame-content {
grid-area: main;
block-size: 100%;
border-radius: var(--mod-app-frame-content-corner-radius, var(--spectrum-app-frame-content-corner-radius)) var(--mod-app-frame-content-corner-radius, var(--spectrum-app-frame-content-corner-radius)) 0 0;
overflow: auto;
position: relative;
container-type: size;
container-name: app-frame-content;
}

/**
* <main> is a separate nested element, in order to allow the option for a document level <footer> element or another aside.
* To-do: provide examples with <footer>. may want to use a class like .spectrum-AppFrame-section-wrapper instead.
*/
.spectrum-AppFrame-content > main {
min-block-size: 100%;
display: grid;
grid-template-rows: max-content 1fr;
}

/* Sections within the content area */
.spectrum-AppFrame-section {
background: var(--highcontrast-app-frame-content-background, var(--mod-app-frame-content-background, var(--spectrum-app-frame-content-background)));
border-radius: var(--mod-app-frame-content-corner-radius, var(--spectrum-app-frame-content-corner-radius));
padding: var(--mod-app-frame-section-padding, var(--spectrum-app-frame-section-padding));

+ .spectrum-AppFrame-section {
margin-block-start: var(--spectrum-app-frame-section-gap);
}
}

.spectrum-AppFrame-content .spectrum-AppFrame-section:last-of-type {
border-radius: var(--mod-app-frame-content-corner-radius, var(--spectrum-app-frame-content-corner-radius)) var(--mod-app-frame-content-corner-radius, var(--spectrum-app-frame-content-corner-radius)) 0 0;
}

/**
* Rounded at the bottom and bottom rounding always visible when there is scrollable content.
*/
.spectrum-AppFrame-content--rounded {
block-size: calc(100% - var(--spectrum-app-frame-to-edge, 0px));
border-radius: var(--mod-app-frame-content-corner-radius, var(--spectrum-app-frame-content-corner-radius));

.spectrum-AppFrame-section:last-of-type {
border-radius: var(--mod-app-frame-content-corner-radius, var(--spectrum-app-frame-content-corner-radius));
}
}

/* Section heading */
.spectrum-AppFrame-section .spectrum-AppFrame-section-heading {
margin-block: var(--spectrum-app-frame-section-heading-margin-block);
max-inline-size: var(--mod-app-frame-max-readable-characters, var(--spectrum-app-frame-max-readable-characters));
}

/* Section inner area; area nested within a section that is spaced out and typically has a section heading within it. */
.spectrum-AppFrame-section-inner:not(:last-child) {
margin-block-end: var(--mod-app-frame-section-inner-area-vertical-gap, var(--spectrum-app-frame-section-inner-area-vertical-gap));
}

/* To-do: not sure about the best selector to apply this limit to. */
.spectrum-AppFrame-section-inner > .spectrum-Heading,
.spectrum-AppFrame-section-inner > .spectrum-Body {
max-inline-size: var(--mod-app-frame-max-readable-characters, var(--spectrum-app-frame-max-readable-characters));
}

/* Featured or onboarding intro section with with branded background */
.spectrum-AppFrame-content-intro {
background: var(--mod-app-frame-intro-background, var(--spectrum-app-frame-intro-background));
}

/* Inner grid that can be used for landing page sections. */
.spectrum-AppFrame-section-grid {
display: grid;
padding: 0;
margin: 0;

/* To-do: not sure about the intended responsiveness, and this should be moddable. */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--mod-app-frame-section-grid-gap, var(--spectrum-app-frame-section-grid-gap));

&:is(ul) {
list-style-type: none;
}
}

/* To-do: not sure about the intended responsiveness, and would ideally use a calculatd min-width with tokens. */
@container (min-width: 700px) {
.spectrum-AppFrame-section-grid > .spectrum-AppFrame-section-grid-item--wide {
grid-column: span 2;
}
}

/* To-do: probably want some borders present on some containers for high contrast mode only. */
@media (forced-colors: active) {
.spectrum-AppFrame {
--highcontrast-app-frame-background: Canvas;
--highcontrast-app-frame-content-background: Canvas;
}
}
51 changes: 51 additions & 0 deletions components/appframe/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "@spectrum-css/appframe",
"version": "0.1.0",
"description": "The Spectrum CSS app frame component",
"license": "Apache-2.0",
"author": "Adobe",
"homepage": "https://opensource.adobe.com/spectrum-css/",
"repository": {
"type": "git",
"url": "https://github.com/adobe/spectrum-css.git",
"directory": "components/appframe"
},
"bugs": {
"url": "https://github.com/adobe/spectrum-css/issues"
},
"exports": {
".": "./dist/index.css",
"./*.md": "./*.md",
"./dist/*": "./dist/*",
"./index-*.css": "./dist/index-*.css",
"./index.css": "./dist/index.css",
"./metadata.json": "./dist/metadata.json",
"./package.json": "./package.json",
"./stories/*": "./stories/*"
},
"main": "dist/index.css",
"peerDependencies": {
"@spectrum-css/tokens": ">=16.1.0-next.0",
"@spectrum-css/typography": ">=9.0.0-next.0"
},
"peerDependenciesMeta": {
"@spectrum-css/tokens": {
"optional": true
},
"@spectrum-css/typography": {
"optional": true
}
},
"keywords": [
"design-system",
"spectrum",
"spectrum-css",
"adobe",
"adobe-spectrum",
"component",
"css"
],
"publishConfig": {
"access": "public"
}
}
17 changes: 17 additions & 0 deletions components/appframe/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"name": "appframe",
"tags": ["component"],
"targets": {
"build": {},
"clean": {},
"compare": {},
"format": {},
"lint": {},
"report": {},
"test": {
"defaultConfiguration": "scope"
},
"validate": {}
}
}
63 changes: 63 additions & 0 deletions components/appframe/stories/appframe.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

import { Template } from "./template.js";

/**
* The app frame component is a starting point for an app's overall layout. It contains the header, side navigation, and main content area.
*
* ⚠️ This is currently an early prototype based on a draft version of design specs.
*/
export default {
title: "App frame",
component: "AppFrame",
argTypes: {
hasSideNavigation: {
name: "Has side navigation",
type: { name: "boolean" },
defaultValue: true,
table: {
type: { summary: "boolean" },
category: "Component",
defaultValue: { summary: "true" },
},
control: "boolean",
},
hasMinimizedSideNav: {
name: "Minimized side navigation",
description: "Displays a horizontally minimized version of the side nav menu items where only the icon is visible.",
table: {
type: { summary: "boolean" },
defaultValue: { summary: false },
category: "Component",
},
control: "boolean",
if: { arg: "hasSideNavigation" },
},
contentLayout: {
name: "Content layout",
description: "Different layout options. The options available here are a work in progress.",
type: { name: "string", required: true },
table: {
type: { summary: "string" },
category: "Component",
},
options: ["default", "rounded"],
control: "select",
},
items: { table: { disable: true } },
},
args: {
rootClass: "spectrum-AppFrame",
hasSideNavigation: true,
hasMinimizedSideNav: false,
contentLayout: "default",
},
parameters: {
layout: "fullscreen",
},
};

/**
* Default, with side navigation
*/
export const Default = Template.bind({});
Default.args = {};
Empty file.
Loading
Loading