Skip to content
Merged
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
1 change: 1 addition & 0 deletions optics-design-system.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"source.fixAll.stylelint": "always",
},
"editor.formatOnSave": true,
"cSpell.words": ["esbenp", "graphviz", "subline", "tintinweb", "unifiedjs"],
},
"extensions": {
"recommendations": [
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rolemodel/optics",
"version": "2.1.4",
"version": "2.1.5",
"packageManager": "yarn@4.8.1",
"description": "Optics is a css package that provides base styles and components that can be integrated and customized in a variety of projects.",
"main": "dist/css/optics.css",
Expand Down
38 changes: 38 additions & 0 deletions src/components/content-header.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.content-header {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: var(--op-space-medium);
padding-block: var(--op-space-medium);

.content-header__details {
display: grid;
gap: var(--op-space-3x-small);
}

.content-header__context {
color: var(--op-color-on-background-alt);
font-size: var(--op-font-small);
}

.content-header__title {
margin: 0;
color: var(--op-color-on-background);
font-size: var(--op-font-2x-large);
font-weight: var(--op-font-weight-medium);
}

.content-header__subline {
color: var(--op-color-on-background-alt);
font-size: var(--op-font-medium);
}

.content-header__aside {
display: flex;
flex-wrap: wrap;
align-items: start;
justify-content: end;
gap: var(--op-space-2x-small);
margin-inline-start: auto;
}
}
1 change: 1 addition & 0 deletions src/components/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
@import 'button_group';
@import 'card';
@import 'confirm-dialog';
@import 'content-header';
@import 'divider';
@import 'form';
@import 'icon';
Expand Down
105 changes: 105 additions & 0 deletions src/stories/Components/ContentHeader/ContentHeader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
const createGitHubExample = () => {
return `
<style>
.content-header.content-header--github-example {
.content-header__subline {
display: flex;
align-items: center;
gap: var(--op-space-2x-small);
font-size: var(--op-font-x-small);
}

.content-header__title-number {
color: var(--op-color-on-background-alt);
}
}
</style>
<header class="content-header content-header--github-example">
<div class="content-header__details">
<h1 class="content-header__title">
Add details to sidebar documentation
<span class="content-header__title-number">#265</span>
</h1>
<div class="content-header__subline">
<span class="badge badge--primary badge--pill">
<span class="material-symbols-outlined icon">graph_1</span>
Merged
</span>
<strong>Jeremy-Walton</strong>
<span>merged 1 commit into</span>
<div class="badge">main</div>
<span>from</span>
<div class="badge">responsive-snippet</div>
<a class="btn btn--no-border btn--icon btn--small" href="#">
<span class="material-symbols-outlined icon">content_copy</span>
</a>
<span>on Jan 31</span>
</div>
</div>
<div class="content-header__aside">
<a class="btn btn--small" href="#">Edit</a>
<a class="btn btn--small" href="#">
<span class="material-symbols-outlined icon">code</span>
Code
<span class="material-symbols-outlined icon">arrow_drop_down</span>
</a>
</div>
</header>
`
}

export const createContentHeader = ({
title = 'Content Header',
showContext = false,
contextLabel = 'Context Label',
showSubline = false,
sublineLabel = 'Subline Label',
showAside = false,
asideExample = 'actions',
githubExample = false,
}) => {
if (githubExample) {
return createGitHubExample()
}

const element = document.createElement('header')

element.className = 'content-header'

let contextContent = ''
let sublineContent = ''

if (showContext) {
contextContent = `<span class='content-header__context'>${contextLabel}</span>`
}

if (showSubline) {
sublineContent += `<span class='content-header__subline'>${sublineLabel}</span>`
}

element.innerHTML = `
<div class='content-header__details'>
${contextContent}
<h1 class='content-header__title'>${title}</h1>
${sublineContent}
</div>
`

if (showAside) {
const asideElement = document.createElement('div')
asideElement.className = 'content-header__aside'
asideElement.innerHTML = '\n <p>Aside text example</p>\n '

if (asideExample === 'actions') {
asideElement.innerHTML = `
<button class="btn">Action 1</button>
<button class="btn">Action 2</button>
`
}
element.appendChild(asideElement)
}

element.innerHTML += '\n'

return element
}
136 changes: 136 additions & 0 deletions src/stories/Components/ContentHeader/ContentHeader.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { Meta, Story, Canvas, Controls } from '@storybook/blocks'
import * as ContentHeaderStories from './ContentHeader.stories'
import { createSourceCodeLink } from '../../helpers/sourceCodeLink.js'

import { createAlert } from '../Alert/Alert.js'

<Meta of={ContentHeaderStories} />

# Content Header

<div
dangerouslySetInnerHTML={{
__html: createSourceCodeLink({ link: 'components/content-header.css' }).outerHTML,
}}
></div>

Content Header classes can be used to denote a page or content section header of an application. They provide simple styles to provide context and actions for a page or section within your interface.

## Playground

<Canvas of={ContentHeaderStories.Default} />
<Controls of={ContentHeaderStories.Default} />

### Selective Imports

Content Header can be used as a standalone component, however, it does have a few dependencies. To see a full dependency list, see [Dependency Graph](?path=/docs/overview-selective-imports--docs#dependencies)

```css
/* Depends on */
@import '@rolemodel/optics/dist/css/core/fonts';
@import '@rolemodel/optics/dist/css/core/tokens';
@import '@rolemodel/optics/dist/css/core/base';

/* Component */
@import '@rolemodel/optics/dist/css/components/content-header';
```

## Variations

### Default

`.content-header` Provides basic content header styles.

`.context-header__details` provides a section for the title, context, and subline.

<Canvas of={ContentHeaderStories.Default} />

### With Context

`.context-header__context` provides a context label above the title.

<Canvas of={ContentHeaderStories.WithContext} />

### With Subline

`.context-header__subline` provides a subline below the title.

<Canvas of={ContentHeaderStories.WithSubline} />

### With Aside

`.context-header__aside` provides an aside section additional information or actions. It can hold anything you want, but is often used for page specific actions.

<Canvas of={ContentHeaderStories.WithAside} />

### Simple

The title is really the only required part of a content header, though only really meaningful when used with actions, context, or subline.

<Canvas of={ContentHeaderStories.Simple} />

### GitHub Example

This example demonstrates a content header with minimal customization achieving a similar look to what might be found on a GitHub pull request page.

<Canvas of={ContentHeaderStories.GitHub} />

## Customizing Content Header styles

<div
dangerouslySetInnerHTML={{
__html: createAlert({
title: 'Important!',
description: 'These patterns represent how to customize the style of the content header for your project.',
}).outerHTML,
}}
></div>

The content header classes are structured using the [BEM methodology](https://getbem.com/naming).

This allows us to define core styles on a main [block](https://getbem.com/naming/#block) class, and use [modifiers](https://getbem.com/naming/#modifier) to encapsulate variant styles. You can modify all content header behavior by overriding the `.content-header` placeholder selector and setting any properties:

```css
.content-header {
padding-block: var(--op-space-3x-large);
}
```

If you need to override the behavior of the header, you can open the class and set or change properties

```css
.content-header--modifier {
padding-block: var(--op-space-2x-large);
}
```

## New Content Header Variations

<div
dangerouslySetInnerHTML={{
__html: createAlert({
title: 'Important!',
description: 'These patterns represent how to create new variations of the content header for your project.',
}).outerHTML,
}}
></div>

Your application may need a variation. To add one, just follow this template. Note the double hyphen, indicating that this is a [modifier](https://getbem.com/naming/#modifier):

```css
.content-header--{name} {
background-color: var(--op-color-primary-on-plus-four);

.content-header__context {
color: var(--op-color-primary-on-plus-four-alt);
}

.content-header__title {
color: var(--op-color-primary-on-plus-four);
}

.content-header__subline {
color: var(--op-color-primary-on-plus-four-alt);
}
}
```
74 changes: 74 additions & 0 deletions src/stories/Components/ContentHeader/ContentHeader.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { createContentHeader } from './ContentHeader.js'

export default {
title: 'Content Components/Content Header',
render: ({ title, ...args }) => {
return createContentHeader({ title, ...args })
},
argTypes: {
title: { control: 'text' },
showContext: { control: 'boolean' },
contextLabel: { if: { arg: 'showContext' }, control: 'text' },
showSubline: { control: 'boolean' },
sublineLabel: { if: { arg: 'showSubline' }, control: 'text' },
showAside: { control: 'boolean' },
asideExample: {
if: { arg: 'showAside' },
control: { type: 'select' },
options: ['actions', 'text'],
},
},
parameters: {
layout: 'padded',
},
}

export const Default = {
args: {
title: 'Content Header',
showContext: true,
contextLabel: 'Context Label',
showSubline: true,
sublineLabel: 'Subline Label',
showAside: true,
asideExample: 'actions',
},
}

export const WithContext = {
args: {
title: 'Content Header',
showContext: true,
contextLabel: 'Context Label',
},
}

export const WithSubline = {
args: {
title: 'Content Header',
showSubline: true,
sublineLabel: 'Subline Label',
},
}

export const WithAside = {
args: {
title: 'Content Header',
showAside: true,
asideExample: 'actions',
},
}

export const Simple = {
args: {
title: 'Only a Title',
showAside: true,
asideExample: 'actions',
},
}

export const GitHub = {
args: {
githubExample: true,
},
}
Loading