Merge branch 'master' into marketing-type-2018
114
.github/CONTRIBUTING.md
vendored
@ -1,9 +1,5 @@
|
||||
## Contributing
|
||||
|
||||
[fork]: https://github.com/primer/primer/fork
|
||||
[pr]: https://github.com/primer/primer/compare
|
||||
[style]: https://styleguide.github.com/primer/principles/
|
||||
|
||||
Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
|
||||
|
||||
## Using the issue tracker
|
||||
@ -75,113 +71,15 @@ Here are a few things you can do that will increase the likelihood of your pull
|
||||
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
|
||||
- Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
||||
|
||||
## Releasing a new Primer version 🎉
|
||||
|
||||
This section is targeted at maintainers of primer, to instruct them on the processes for releasing a new version.
|
||||
|
||||
### In `primer/primer`:
|
||||
|
||||
1. Find or create a new pull request with a release branch from `master` and name it `release-<version>`.
|
||||
|
||||
(CI will publish a release candidate version to npm for branches prefixed with `release`. These version numbers have a `rc.<number>` suffix on them)
|
||||
|
||||
For the pull request you can use the following template.
|
||||
|
||||
```md
|
||||
# Primer Minor Release
|
||||
|
||||
Tracking Issue for next release: 📦 **0.0.0**
|
||||
Approximate release date: 📆
|
||||
|
||||
### Must
|
||||
|
||||
- [ ]
|
||||
|
||||
### Should
|
||||
|
||||
- [ ]
|
||||
|
||||
### Could
|
||||
|
||||
- [ ]
|
||||
|
||||
----
|
||||
|
||||
### Ship checklist
|
||||
|
||||
- [ ] Update CHANGELOG
|
||||
- [ ] Run version bump
|
||||
- [ ] Update primer.github.io
|
||||
- [ ] Update github/github
|
||||
- [ ] Update the style guide
|
||||
- [ ] Update the release tag note
|
||||
- [ ] Create a new pull request for the next release
|
||||
|
||||
/cc @primer/ds-core
|
||||
```
|
||||
|
||||
2. Go through the tracking issue and make sure everything that should be merged in is merged in.
|
||||
|
||||
3. Once your builds finish, click on the details links for the continuous-integration/travis-ci/push build. Expand the `Deploying application` output and you should be able to find an outputted change log here. Copy this and update the [CHANGELOG.md](https://github.com/primer/primer/blob/master/CHANGELOG.md) file.
|
||||
|
||||
4. Run the version bump in your terminal: `npm run bump`.
|
||||
|
||||
5. Test your changes with the latest release candidate version in the appropriate places (styleguide, storybook, github/github).
|
||||
|
||||
6. Once the release PR is approved and you've done necessary testing, merge to `master`. This will trigger a publish to npm.
|
||||
|
||||
### In `github/github`:
|
||||
|
||||
1. Create a new branch
|
||||
|
||||
2. Update the primer version in your terminal `bin/npm install primer@<version>`.
|
||||
|
||||
3. Update `stylelint-config-primer` in your terminal to the appropriate version `bin/npm install stylelint-config-primer@latest`.
|
||||
|
||||
4. If you need to make changes to github/github due to the Primer release, make a separate branch. When ready, merge that branch into your release branch.
|
||||
|
||||
5. Add reviewers.
|
||||
|
||||
6. Check that every deleted vendor file has an accompanying updated vendor file and that the version numbers look correct.
|
||||
|
||||
7. Test on review-lab.
|
||||
|
||||
8. When ready, merge! 🎉
|
||||
|
||||
|
||||
## Other items that need to be done after publishing Primer
|
||||
|
||||
#### Update the Style Guide
|
||||
|
||||
1. In [github/styleguide](https://github.com/github/styleguide), update `primer` to your newly released version in your terminal:
|
||||
|
||||
`npm install primer@latest`
|
||||
|
||||
2. Then run: `script/update-primer-docs`.
|
||||
|
||||
3. Commit changes, make PR, get it approved, merge! 🚀
|
||||
|
||||
#### Update [primer.github.io](primer.github.io)
|
||||
|
||||
1. Edit [index.html](https://github.com/primer/primer.github.io/blob/master/index.html) to include the latest version.
|
||||
|
||||
#### Update Storybook
|
||||
|
||||
1. Pull the latest from master on primer/primer (after merging in release branch).
|
||||
|
||||
2. Run `npm run publish-storybook`.
|
||||
|
||||
#### Publish release tag
|
||||
|
||||
1. Create a new release tag [here](https://github.com/primer/primer/releases/new).
|
||||
|
||||
2. Copy the changes from the [CHANGELOG](https://github.com/primer/primer/blob/master/CHANGELOG.md) and paste it into the release notes.
|
||||
|
||||
3. Publish 🎉
|
||||
|
||||
## Releasing a new Primer version
|
||||
See [RELEASING.md](../RELEASING.md) for our release process.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Contributing to Open Source on GitHub](https://guides.github.com/activities/contributing-to-open-source/)
|
||||
- [Using Pull Requests](https://help.github.com/articles/using-pull-requests/)
|
||||
- [GitHub Help](https://help.github.com)
|
||||
|
||||
[fork]: https://github.com/primer/primer/fork
|
||||
[pr]: https://github.com/primer/primer/compare
|
||||
[style]: https://styleguide.github.com/primer/principles/
|
||||
|
14
.gitignore
vendored
@ -1,8 +1,10 @@
|
||||
.DS_Store
|
||||
.sass-cache
|
||||
node_modules
|
||||
*.log
|
||||
build
|
||||
_site
|
||||
*.lerna_backup
|
||||
*.log
|
||||
*/*/package-lock.json
|
||||
.DS_Store
|
||||
.changelog
|
||||
.sass-cache
|
||||
_site
|
||||
build
|
||||
primer-version.txt
|
||||
node_modules
|
||||
|
@ -1,65 +1,27 @@
|
||||
import remark from 'remark'
|
||||
import parents from 'unist-util-parents'
|
||||
import select from 'unist-util-select'
|
||||
import findBefore from 'unist-util-find-before'
|
||||
import parseCodeBlocks from 'code-blocks/lib/fromString'
|
||||
import htmlToReact from 'html-to-react'
|
||||
import parsePairs from 'parse-pairs'
|
||||
import React from 'react'
|
||||
import ReactDOMServer from 'react-dom/server'
|
||||
import {Octicon} from '../Octicon'
|
||||
|
||||
const htmlParser = new htmlToReact.Parser()
|
||||
|
||||
const railsOcticonToReact = (html) => {
|
||||
// <%= octicon "tools" %> to <Octicon name="tools" />
|
||||
const octre = /<%= octicon[\(\s]["']([a-z\-]+)["'][^%]*%>/gi
|
||||
html = html.replace(octre, (match, name) => {
|
||||
return ReactDOMServer.renderToStaticMarkup(<Octicon name={name} />)
|
||||
})
|
||||
return html
|
||||
}
|
||||
|
||||
const parseBlockAttrs = (node, file) => {
|
||||
const pairs = node.lang.replace(/^html\s*/, '')
|
||||
const attrs = pairs.length ? parsePairs(pairs) : {}
|
||||
attrs.title = attrs.title
|
||||
|| getPreviousHeading(node)
|
||||
|| `story @ ${file}:${node.position.start.line}`
|
||||
node.block = attrs
|
||||
return node
|
||||
}
|
||||
|
||||
const nodeToStory = (node, file) => {
|
||||
const html = railsOcticonToReact(node.value)
|
||||
const {title} = node.block
|
||||
const blockToStory = block => {
|
||||
return {
|
||||
title,
|
||||
story: () => htmlParser.parse(html),
|
||||
html,
|
||||
file,
|
||||
node,
|
||||
title: block.title,
|
||||
story: () => htmlParser.parse(block.value),
|
||||
block,
|
||||
}
|
||||
}
|
||||
|
||||
const getPreviousHeading = node => {
|
||||
const heading = findBefore(node.parent, node, 'heading')
|
||||
return (heading && !heading.used)
|
||||
? (heading.used = true, heading.children.map(c => c.value).join(''))
|
||||
: undefined
|
||||
}
|
||||
|
||||
export default req => {
|
||||
return req.keys()
|
||||
.filter(file => !file.match(/node_modules/))
|
||||
.reduce((stories, file) => {
|
||||
const content = req(file)
|
||||
const ast = parents(remark.parse(content))
|
||||
const path = file.replace(/^\.\//, '')
|
||||
return stories.concat(
|
||||
select(ast, 'code[lang^=html]')
|
||||
.map(parseBlockAttrs)
|
||||
.filter(({block}) => block.story !== "false")
|
||||
.map(node => nodeToStory(node, path))
|
||||
)
|
||||
}, [])
|
||||
export default function storiesFromMarkdown(req) {
|
||||
return req.keys().reduce((stories, file) => {
|
||||
const markdown = req(file)
|
||||
const path = file.replace(/^\.\//, '')
|
||||
const blocks = parseCodeBlocks(markdown, path)
|
||||
.filter(block => {
|
||||
// read: ```html *
|
||||
// skip: ```html * story="false"
|
||||
return block.lang === 'html' && block.info.story !== 'false'
|
||||
})
|
||||
.map(blockToStory)
|
||||
return stories.concat(blocks)
|
||||
}, [])
|
||||
}
|
||||
|
19
.travis.yml
@ -1,6 +1,6 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 7
|
||||
- 8
|
||||
|
||||
env:
|
||||
global:
|
||||
@ -13,28 +13,19 @@ script:
|
||||
- npm test
|
||||
- script/check-versions
|
||||
|
||||
after_success:
|
||||
before_deploy:
|
||||
# this will short-circuit the publish step if it fails to interpolate $NPM_API_KEY
|
||||
- npm config set "//registry.npmjs.org/:_authToken=\${NPM_API_KEY}"
|
||||
# copy the CHANGELOG.md primer for publishing
|
||||
- cp CHANGELOG.md modules/primer
|
||||
- script/after_success
|
||||
|
||||
deploy:
|
||||
# publish release candidates on release branches
|
||||
# publish canary releases on all branches *except* master and release-*
|
||||
- provider: script
|
||||
script: script/release-candidate
|
||||
script: script/deploy --yes
|
||||
skip_cleanup: true
|
||||
on:
|
||||
branch: release*
|
||||
tags: false
|
||||
|
||||
# publish "final" releases on master
|
||||
- provider: script
|
||||
script: script/release
|
||||
skip_cleanup: true
|
||||
on:
|
||||
branch: master
|
||||
all_branches: true
|
||||
tags: false
|
||||
|
||||
notifications:
|
||||
|
109
CHANGELOG.md
@ -1,3 +1,112 @@
|
||||
# 10.10.5
|
||||
|
||||
#### :bug: Bug Fix
|
||||
* [#650](https://github.com/primer/primer/pull/650) Fix border radius edge utility specificity. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### :memo: Documentation
|
||||
* [#649](https://github.com/primer/primer/pull/649) Sandboxed code examples. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### :house: Internal
|
||||
- Only check links on Travis if `[check-links]` is included in the commit message
|
||||
- a5658d3 Run `now alias` without the branch name on merge to `master`
|
||||
|
||||
#### Committers: 1
|
||||
- Shawn Allen ([shawnbot](https://github.com/shawnbot))
|
||||
|
||||
|
||||
# 10.10.4
|
||||
|
||||
#### :memo: Documentation
|
||||
* [#642](https://github.com/primer/primer/pull/642) docs: add Ash's new header illustration. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### :house: Internal
|
||||
* [#641](https://github.com/primer/primer/pull/641) test(docs): improve style guide URL path test. ([@shawnbot](https://github.com/shawnbot))
|
||||
* [#635](https://github.com/primer/primer/pull/635) docs: Releases link, Status key page move. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### Committers: 1
|
||||
- Shawn Allen ([shawnbot](https://github.com/shawnbot))
|
||||
|
||||
# 10.10.3
|
||||
|
||||
#### :memo: Documentation
|
||||
* [#632](https://github.com/primer/primer/pull/632) Happy new year! ([@shawnbot](https://github.com/shawnbot))
|
||||
* [#626](https://github.com/primer/primer/pull/626) Branch deployment, docs for the docs. ([@shawnbot](https://github.com/shawnbot))
|
||||
* [#616](https://github.com/primer/primer/pull/616) Start up the docs directory. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### :house: Internal
|
||||
* [#631](https://github.com/primer/primer/pull/631) Docs release fixes. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### Committers: 2
|
||||
- Emily Brick ([emilybrick](https://github.com/emilybrick))
|
||||
- Shawn Allen ([shawnbot](https://github.com/shawnbot))
|
||||
|
||||
# 10.10.2
|
||||
|
||||
#### :memo: Documentation
|
||||
* [#614](https://github.com/primer/primer/pull/614) fix broken border-radius helper example. ([@joelhawksley](https://github.com/joelhawksley))
|
||||
|
||||
#### :house: Internal
|
||||
* [#615](https://github.com/primer/primer/pull/615) pin npm-run-all@4.1.5. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### Committers: 2
|
||||
- Joel Hawksley ([joelhawksley](https://github.com/joelhawksley))
|
||||
- Shawn Allen ([shawnbot](https://github.com/shawnbot))
|
||||
|
||||
# 10.10.1
|
||||
|
||||
#### :memo: Documentation
|
||||
* [#606](https://github.com/primer/primer/pull/606) Fix for Progress Broken Package Link. ([@emilybrick](https://github.com/emilybrick))
|
||||
|
||||
#### :house: Internal
|
||||
* [#608](https://github.com/primer/primer/pull/608) Update releasing docs. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### Committers: 2
|
||||
- Emily Brick ([emilybrick](https://github.com/emilybrick))
|
||||
- Shawn Allen ([shawnbot](https://github.com/shawnbot))
|
||||
|
||||
# 10.10.0
|
||||
|
||||
#### :rocket: Enhancement
|
||||
* [#573](https://github.com/primer/primer/pull/573) Add Progress component. ([@emilybrick](https://github.com/emilybrick))
|
||||
* [#561](https://github.com/primer/primer/pull/561) Add HTML `hidden` attribute docs, increase `[hidden]` selector specificity. ([@shawnbot](https://github.com/shawnbot) h/t @jonrohan)
|
||||
|
||||
#### :bug: Bug Fix
|
||||
* [#604](https://github.com/primer/primer/pull/604) Fix Button group focus ring z-index issues. ([@shawnbot](https://github.com/shawnbot))
|
||||
* [#570](https://github.com/primer/primer/pull/570) Make `.blankslate-narrow` responsive. ([@crhallberg](https://github.com/crhallberg))
|
||||
* [#591](https://github.com/primer/primer/pull/591) Add fs-extra to `primer-module-build.dependencies`. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### :memo: Documentation
|
||||
* [#585](https://github.com/primer/primer/pull/585) Improve contributing docs and add DEVELOP.md. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### :house: Internal
|
||||
* [#597](https://github.com/primer/primer/pull/597) Fix primerize, add "fresh" run-script, etc. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### Committers: 3
|
||||
- Chris Hallberg ([crhallberg](https://github.com/crhallberg))
|
||||
- Emily Brick ([emilybrick](https://github.com/emilybrick))
|
||||
- Shawn Allen ([shawnbot](https://github.com/shawnbot))
|
||||
|
||||
# 10.9.0
|
||||
#### :rocket: Enhancement
|
||||
* [#586](https://github.com/primer/primer/pull/586) Hiding .Counter component when it's empty.. ([@jonrohan](https://github.com/jonrohan))
|
||||
* [#545](https://github.com/primer/primer/pull/545) Simplify responsive utilities with $responsive-variants. ([@shawnbot](https://github.com/shawnbot))
|
||||
* [#557](https://github.com/primer/primer/pull/557) Add !important to [hidden]. ([@muan](https://github.com/muan))
|
||||
|
||||
#### :memo: Documentation
|
||||
* [#580](https://github.com/primer/primer/pull/580) Remove invalid button classes. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### :house: Internal
|
||||
* [#581](https://github.com/primer/primer/pull/581) Use code-blocks. ([@shawnbot](https://github.com/shawnbot))
|
||||
* [#530](https://github.com/primer/primer/pull/530) Adding user details to storybook publish script. ([@jonrohan](https://github.com/jonrohan))
|
||||
* [#579](https://github.com/primer/primer/pull/579) Upgrade to lerna@2.11, rebuild package-lock. ([@shawnbot](https://github.com/shawnbot))
|
||||
|
||||
#### Committers: 5
|
||||
- Jon Rohan ([jonrohan](https://github.com/jonrohan))
|
||||
- Mickaël Derriey ([mderriey](https://github.com/mderriey))
|
||||
- Mu-An Chiou ([muan](https://github.com/muan))
|
||||
- Shawn Allen ([shawnbot](https://github.com/shawnbot))
|
||||
- Sophie Shepherd ([sophshep](https://github.com/sophshep))
|
||||
|
||||
# 10.8.1
|
||||
#### :bug: Bug Fix
|
||||
* [#554](https://github.com/primer/primer/pull/554) Fixes peer dependency issues ([@emplums](https://github.com/emplums))
|
||||
|
76
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at emilybrick@github.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
88
DEVELOP.md
Normal file
@ -0,0 +1,88 @@
|
||||
# Primer Development
|
||||
|
||||
If you've made it this far, **thank you**! We appreciate your contribution, and hope that this document helps you along the way.
|
||||
|
||||
## Structure
|
||||
The project is structured as a [monorepo] made up of lots of small npm modules, many of which depend on each other. We use [Lerna] to manage, version, and publish all of the packages together.
|
||||
|
||||
The top-level `package.json` is not published, but tracks common dependencies for developing Primer, and hosts some useful npm [run-scripts]. See the [scripts section](#scripts) for more info.
|
||||
|
||||
## Workflow
|
||||
The typical Primer workflow looks something like this:
|
||||
|
||||
1. [Install](#install)
|
||||
2. [Start Storybook](#storybook)
|
||||
3. Navigate to the module you're working on and modify the SCSS and/or markdown files.
|
||||
4. Test your changes in Storybook.
|
||||
5. Push your work to a new branch to test it on Travis and have it reviewed by the Primer "core" team.
|
||||
|
||||
## Install
|
||||
Run `npm install` to install the npm dependencies and automatically run link all of the local packages together with `npm run bootstrap`.
|
||||
|
||||
### Troubleshooting install problems
|
||||
If you run into trouble installing, it's always best to ensure that you're starting from a clean slate by running the following from the repository root directory:
|
||||
|
||||
```sh
|
||||
npm run fresh
|
||||
```
|
||||
|
||||
If _that_ gives you problems, then you can try manually deleting everything and starting over:
|
||||
|
||||
```
|
||||
rm -rf node_modules
|
||||
rm -f package-lock.json */*/package-lock.json
|
||||
npm install
|
||||
```
|
||||
|
||||
**You may need to do this whenever switching between branches with different dependencies, submodules, or versions of Node and/or npm.** The Primer core team generally uses the latest major version of Node (10 as of this writing), but our CI tests run Node 8 and npm 6. You can check which versions you're running with:
|
||||
|
||||
```sh
|
||||
npm --version
|
||||
node --version
|
||||
```
|
||||
|
||||
## Storybook
|
||||
Run `npm start` to start up [Storybook], then visit [localhost:3000](http://localhost:3000) to test your work. By default, all `html` code blocks of all `*.md` files in each module will be rendered as stories and listed under the module's name in the left-hand nav. File changes should trigger live reload automatically after a brief delay.
|
||||
|
||||
If the package you're working on has a `stories.js`, it probably includes a snippet like this:
|
||||
|
||||
```js
|
||||
const stories = storiesOf('Module name', module)
|
||||
|
||||
storiesFromMarkdown(require.context('.', true, /\.md$/))
|
||||
.forEach(({title, story}) => {
|
||||
stories.add(title, story)
|
||||
})
|
||||
```
|
||||
|
||||
This is how we find all of the Markdown files in the package directory and generate stories from their code blocks. Storybook sections are labeled by the first argument to `storiesOf()` (in the above example, "Module name"), and individual stories get their titles from either the previous Markdown heading or the `title` attribute in the fenced code block. See the [`code-blocks` docs](https://npmjs.com/package/code-blocks) and the [`storiesFromMarkdown()` source](./.storybook/lib/storiesFromMarkdown.js) for more info.
|
||||
|
||||
## CSS packages
|
||||
All of the Primer CSS packages live in the [modules](./modules) subdirectory, including the [`primer`](./modules/package) omnibus package.
|
||||
|
||||
## Tools
|
||||
Many tools specific to development of Primer CSS live in the [tools](./tools) subdirectory.
|
||||
|
||||
## Scripts
|
||||
The [`script` directory](./script) houses a collection of scripts that we use to maintain, test, build, and publish packages. Some scripts of note:
|
||||
|
||||
* `script/check-imports` compares the list of Primer npm dependencies for each package with SCSS `@import` statements in its source, and warns if any mismatches (dependencies without corresponding imports, or vice-versa) are found.
|
||||
* `script/compare-published` compares the latest published versions of each Primer CSS package with the `version` field in its local `package.json`, and reports any discrepancies.
|
||||
* `script/get-packages` lists all of the package subdirectories from both `modules` and `tools` directories, and is useful for iterating in shell scripts:
|
||||
|
||||
```sh
|
||||
for pkg in $(script/get-packages); do
|
||||
echo $pkg
|
||||
done
|
||||
```
|
||||
|
||||
If you're looking for more detail, you can also run `npx lerna ls`, which will list the packages by name along with their versions.
|
||||
|
||||
Scripts like `lint-scss`, `notify`, and `test-docs` are called from individual packages to run specific common tasks; `npm-run` and `npm-run-all` are used more generally to run monorepo-installed npm utilities within the package directory, and can probably be refactored to simply run [npx].
|
||||
|
||||
|
||||
[monorepo]: https://github.com/babel/babel/blob/master/doc/design/monorepo.md
|
||||
[Lerna]: https://github.com/lerna/lerna
|
||||
[run-scripts]: https://docs.npmjs.com/cli/run-script
|
||||
[Storybook]: https://storybook.js.org/
|
||||
[npx]: https://www.npmjs.com/package/npx
|
20
README.md
@ -46,35 +46,21 @@ Then, you would import the module with:
|
||||
@import "primer-navigation/index.scss";
|
||||
```
|
||||
|
||||
Or, while you're figuring out which modules you need, you can import them directly from the `primer` [`modules` directory](./modules) like so:
|
||||
|
||||
```scss
|
||||
@import "primer/modules/primer-navigation/index.css";
|
||||
```
|
||||
|
||||
|
||||
## Build
|
||||
|
||||
For a compiled **CSS** version of this module, an npm script is included that will output a CSS version to `build/build.css`. The built CSS file is also included in the npm package.
|
||||
|
||||
```sh
|
||||
$ npm run build
|
||||
```
|
||||
## Development
|
||||
See [DEVELOP.md](./DEVELOP.md) for development docs.
|
||||
|
||||
## Releasing (Staff only)
|
||||
|
||||
You can find docs about our release process in [RELEASING.md](./RELEASING.md).
|
||||
|
||||
## Documentation
|
||||
|
||||
You can read more about primer in the [docs][docs].
|
||||
Primer CSS documentation is published to the [GitHub Style Guide](https://styleguide.github.com/primer/).
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE) © [GitHub](https://github.com/)
|
||||
|
||||
[primer]: https://github.com/primer/primer
|
||||
[docs]: http://primer.github.io/
|
||||
[npm]: https://www.npmjs.com/
|
||||
[install-npm]: https://docs.npmjs.com/getting-started/installing-node
|
||||
[sass]: http://sass-lang.com/
|
||||
|
83
RELEASING.md
@ -4,21 +4,69 @@
|
||||
### In `primer/primer`:
|
||||
|
||||
|
||||
1. Go through the tracking PR and make sure everything that should be merged in is merged in.
|
||||
1. Go through the tracking PR and make sure everything listed is merged in.
|
||||
|
||||
2. To update the change log for your release, click on the details links for the continuous-integration/travis-ci/push build. Expand the `Deploying application` output and copy the change log content. Update the [CHANGELOG.md](https://github.com/primer/primer/blob/master/CHANGELOG.md) file with the change log content from the build.
|
||||
2. To update the changelog for your release, click on the details links for the continuous-integration/travis-ci/push build. Expand the `Deploying application` output and copy the changelog content. Update the [CHANGELOG.md](https://github.com/primer/primer/blob/master/CHANGELOG.md) file with the changelog content from build
|
||||
|
||||
3. Run the version bump in your terminal: `npm run bump`.
|
||||
**Note**: the CHANGELOG contents may be hidden within a collapsed section of the Travis logs under `Deploying the application`. Click the ▶ to the left of that section to expand it:
|
||||
|
||||
![image](https://user-images.githubusercontent.com/113896/48871307-0be2eb00-ed99-11e8-97ab-b9119ac4b7d3.png)
|
||||
|
||||
4. Run `script/check-versions` to double check there are no version conflicts. You may need to update peer dependencies in `primer-popover` and `primer-marketing-buttons`.
|
||||
Then scroll to the bottom of the page, and copy all of the text between the `Unreleased (YYYY-MM-DD)` heading and the exit status message. You may need to copy _before_ releasing your mouse to prevent Travis from collapsing that section of the logs first:
|
||||
|
||||
![image](https://user-images.githubusercontent.com/113896/48871298-f7065780-ed98-11e8-9160-c1016d61d042.png)
|
||||
|
||||
3. Bump the package versions in your terminal:
|
||||
|
||||
```sh
|
||||
npm run bump
|
||||
```
|
||||
|
||||
4. Run `script/check-versions` to catch any cross-module version mismatches. (This will run on Travis, too.)
|
||||
|
||||
5. Test your changes with the latest release candidate version in the appropriate places (styleguide, storybook, github/github).
|
||||
|
||||
6. Once the release PR is approved and you've done necessary testing, merge to `master`. This will trigger a publish to npm.
|
||||
6. Once the release PR is approved and you've done necessary testing, merge to `master`. This will trigger publishing to npm.
|
||||
|
||||
7. Create a new release branch for the next release from `master` and name it `release-<version>`.
|
||||
7. Create a new release branch for the next release from `master` and name it `release-<version>`. Please use the following template for the PR description, linking to the relevant issues and/or pull requests for each change, and removing irrelevant headings:
|
||||
|
||||
(CI will publish a release candidate version to npm for branches prefixed with `release`. These version numbers have a `rc.<number>` suffix on them)
|
||||
```md
|
||||
# Primer [Major|Minor|Patch] Release
|
||||
|
||||
Version: 📦 **0.0.0**
|
||||
Approximate release date: 📆 DD/MM/YY
|
||||
|
||||
### :boom: Breaking Change
|
||||
- [ ] Description #
|
||||
|
||||
### :rocket: Enhancement
|
||||
- [ ] Description #
|
||||
|
||||
### :bug: Bug Fix
|
||||
- [ ] Description #
|
||||
|
||||
### :nail_care: Polish
|
||||
- [ ] Description #
|
||||
|
||||
### :memo: Documentation
|
||||
- [ ] Description #
|
||||
|
||||
### :house: Internal
|
||||
- [ ] Description #
|
||||
|
||||
----
|
||||
|
||||
### Ship checklist
|
||||
|
||||
- [ ] Update `CHANGELOG.md`
|
||||
- [ ] Bump versions with `npm run bump`
|
||||
- [ ] [Create a new release](https://github.com/primer/primer/releases/new)
|
||||
- [ ] [Update github/github](https://github.com/primer/primer/blob/master/RELEASING.md#in-githubgithub)
|
||||
- [ ] [Update github/styleguide](https://github.com/github/styleguide/#adding-new-content-from-primer)
|
||||
- [ ] Create a new pull request for the next release
|
||||
|
||||
/cc @primer/ds-core
|
||||
```
|
||||
|
||||
|
||||
### In `github/github`:
|
||||
@ -40,26 +88,9 @@
|
||||
8. When ready, merge! 🎉
|
||||
|
||||
|
||||
## Other items that need to be done after publishing Primer
|
||||
### Publish release tag
|
||||
|
||||
#### Update the Style Guide
|
||||
|
||||
1. In [github/styleguide](https://github.com/github/styleguide), update `primer` to your newly released version in your terminal:
|
||||
|
||||
`npm install primer@latest`
|
||||
|
||||
2. Then run: `script/update-primer-docs`.
|
||||
|
||||
3. Commit changes, make PR, get it approved, merge! 🚀
|
||||
|
||||
#### Update [primer.github.io](primer.github.io)
|
||||
|
||||
1. Edit [index.html](https://github.com/primer/primer.github.io/blob/master/index.html) to include the latest version.
|
||||
|
||||
|
||||
#### Publish release tag
|
||||
|
||||
1. Create a new release tag [here](https://github.com/primer/primer/releases/new).
|
||||
1. [Create a new release](https://github.com/primer/primer/releases/new) with tag `v<version>`.
|
||||
|
||||
2. Copy the changes from the [CHANGELOG](https://github.com/primer/primer/blob/master/CHANGELOG.md) and paste it into the release notes.
|
||||
|
||||
|
15
docs/.eslintrc.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": [
|
||||
"plugin:github/es6",
|
||||
"plugin:github/react",
|
||||
"plugin:jsx-a11y/recommended"
|
||||
],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "^16.6"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"import/no-namespace": 0
|
||||
}
|
||||
}
|
2
docs/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.next/
|
||||
static/primer.css
|
58
docs/README.md
Normal file
@ -0,0 +1,58 @@
|
||||
# Primer CSS docs site
|
||||
This directory hosts a [Next] site that pulls in Primer CSS documentation from the [modules directory](../modules).
|
||||
|
||||
## Running the site
|
||||
First, make sure that you've bootstrapped the monorepo from the top-level directory. The `fresh` run-script will ensure that all `node_modules` or `package-lock.json` files are removed first:
|
||||
|
||||
```
|
||||
# in the repo root
|
||||
npm run fresh
|
||||
```
|
||||
|
||||
Then, navigate to this directory (`cd docs`) and run:
|
||||
|
||||
```sh
|
||||
# in the docs directory
|
||||
npm run dev
|
||||
```
|
||||
|
||||
This should start up the Next dev server and a background task that will keep the `pages` directory up-to-date whenever you change the source files in `modules/primer*`.
|
||||
|
||||
## Syncing the docs
|
||||
If, for whatever reason, the dev server isn't syncing files, you have two choices:
|
||||
|
||||
1. Stop the server (`ctrl-C`) and restart it (`npm run dev`), which will re-sync the files and clear Next's cache.
|
||||
2. Run [script/sync](./script/sync) manually:
|
||||
|
||||
```sh
|
||||
# in the docs directory
|
||||
script/sync
|
||||
```
|
||||
|
||||
**If you find yourself needing to do this often, please [file an issue](/primer/primer/issues/new) and tag `@shawnbot`**. :bow:
|
||||
|
||||
## The pages directory
|
||||
The [pages directory](./pages/) contains all of the files that map to URLs on the site. Because we plan to host the site at `primer.style/css` (and because of the way that Now's path aliasing feature works), we nest all of our documentation under the additional [css directory](./pages/css).
|
||||
|
||||
The sync task maintains a listing of files that it's copied from the modules directory in `pages/css/.gitignore`, which ensures that none of these files are checked into git.
|
||||
|
||||
If you find yourself editing a file,
|
||||
|
||||
## Sync internals
|
||||
|
||||
We use [Metalsmith] to sync the source docs to the `pages` directory and transform them in the following ways:
|
||||
|
||||
1. We filter the list of files to only Markdown documents and `package.json` files
|
||||
1. Many package `README.md`s wrap the actual documentation content in `<!-- %docs -->` HTML comments that usually include YAML frontmatter. In these instances, we extract the content that portion and reformat the frontmatter.
|
||||
1. We filter out any Markdown files that _don't_ include a `path` frontmatter key, and rename the destination file to match the `path` (e.g. `path: foo/bar` writes to `pages/css/foo/bar.md`).
|
||||
1. We set the `source` frontmatter key to a fully-qualified `github.com` URL for the source file so that we can link directly to it.
|
||||
1. A limited list of fields for all packages is extracted into a single file (`pages/css/packages.json`), which serves as a light-weight dependency graph.
|
||||
1. We read [the changelog](../CHANGELOG.md) and write it to `whats-new/changelog.md` with some additional frontmatter.
|
||||
1. We read the list of files from `pages/css/.gitignore` and delete them from the filesystem, then write the new list of paths so that they aren't committed to git.
|
||||
|
||||
All of the logic for syncing the source docs (and transforming them in transit) is controlled in [`lib/sync.js`](./lib/sync.js), and each "step" in the transformation (as well as the watching) is implemented as a Metalsmith plugin.
|
||||
|
||||
**Why [Metalsmith]?** We're glad you asked! `@shawnbot` likes the simplicity of Metalsmith's core and how easy it is to write powerful plugins.
|
||||
|
||||
[Metalsmith]: https://metalsmith.io/
|
||||
[Next]: https://github.com/zeit/next.js
|
44
docs/lib/add-package-meta.js
Normal file
@ -0,0 +1,44 @@
|
||||
const {existsSync} = require('fs')
|
||||
const {dirname, join, resolve} = require('path')
|
||||
|
||||
const cache = {}
|
||||
|
||||
module.exports = function addPackageMeta(options = {}) {
|
||||
const {fields, namespace = 'data', log = noop} = options
|
||||
return (files, metal, done) => {
|
||||
const root = metal.source()
|
||||
for (const [path, file] of Object.entries(files)) {
|
||||
const pkg = getPackageRelativeTo(path, root)
|
||||
if (pkg) {
|
||||
file[namespace].package = fields ? pluck(pkg, fields) : pkg
|
||||
} else {
|
||||
log('no package.json found relative to', path)
|
||||
}
|
||||
}
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
function getPackageRelativeTo(file, root) {
|
||||
let dir = join(root, dirname(file))
|
||||
if (dir in cache) {
|
||||
return cache[dir]
|
||||
}
|
||||
while (dir !== root) {
|
||||
const pkgPath = join(dir, 'package.json')
|
||||
if (existsSync(pkgPath)) {
|
||||
return (cache[dir] = require(pkgPath))
|
||||
}
|
||||
dir = resolve(dir, '..')
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function pluck(data, fields) {
|
||||
return fields.reduce((out, field) => {
|
||||
out[field] = data[field]
|
||||
return out
|
||||
}, {})
|
||||
}
|
||||
|
||||
function noop() {}
|
16
docs/lib/add-source.js
Normal file
@ -0,0 +1,16 @@
|
||||
const each = require('./each')
|
||||
|
||||
module.exports = function addSource(options = {}) {
|
||||
const {namespace = 'data'} = options
|
||||
for (const key of ['branch', 'repo']) {
|
||||
if (!options[key]) {
|
||||
throw new Error(`addSource() plugin requires options.${key} (got ${JSON.stringify(options[key])})`)
|
||||
}
|
||||
}
|
||||
const {branch, repo} = options
|
||||
return each((file, source) => {
|
||||
if (file[namespace]) {
|
||||
file[namespace].source = `https://github.com/${repo}/tree/${branch}/modules/${source}`
|
||||
}
|
||||
})
|
||||
}
|
71
docs/lib/config.js
Normal file
@ -0,0 +1,71 @@
|
||||
/* eslint-disable no-console */
|
||||
const sync = require('./sync')
|
||||
const cssLoaderConfig = require('@zeit/next-css/css-loader-config')
|
||||
const {CI, NODE_ENV, NOW_URL} = process.env
|
||||
|
||||
module.exports = (nextConfig = {}) => {
|
||||
const {assetPrefix = NOW_URL || ''} = nextConfig
|
||||
|
||||
let configured = false
|
||||
|
||||
return Object.assign({}, nextConfig, {
|
||||
assetPrefix,
|
||||
pageExtensions: ['js', 'jsx', 'md', 'mdx'],
|
||||
|
||||
publicRuntimeConfig: Object.assign({
|
||||
assetPrefix,
|
||||
production: NODE_ENV === 'production'
|
||||
}, nextConfig.publicRuntimeConfig),
|
||||
|
||||
webpack(config, options) {
|
||||
if (!options.defaultLoaders) {
|
||||
throw new Error(
|
||||
'This plugin is not compatible with Next.js versions below 5.0.0 https://err.sh/next-plugins/upgrade'
|
||||
)
|
||||
}
|
||||
|
||||
const {dev, isServer} = options
|
||||
|
||||
// only attempt to sync locally and in CI
|
||||
if (dev && !configured) {
|
||||
sync({watch: !CI})
|
||||
}
|
||||
|
||||
// in production, we don't need to compile Primer from SCSS; just inline
|
||||
// the CSS build!
|
||||
if (!dev) {
|
||||
config.resolve.alias['primer/index.scss$'] = require.resolve('primer/build/build.css')
|
||||
|
||||
const cssLoader = cssLoaderConfig(config, {
|
||||
dev,
|
||||
isServer
|
||||
})
|
||||
options.defaultLoaders.css = cssLoader
|
||||
config.module.rules.push({
|
||||
test: /\.css$/,
|
||||
loader: cssLoader
|
||||
})
|
||||
}
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.svg$/,
|
||||
use: '@svgr/webpack'
|
||||
})
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.mdx?$/,
|
||||
use: [
|
||||
options.defaultLoaders.babel,
|
||||
require.resolve('./mdx-loader')
|
||||
]
|
||||
})
|
||||
|
||||
configured = true
|
||||
if (typeof nextConfig.webpack === 'function') {
|
||||
return nextConfig.webpack(config, options)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
})
|
||||
}
|
8
docs/lib/each.js
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports = function each(fn) {
|
||||
return (files, metal, done) => {
|
||||
for (const path of Object.keys(files)) {
|
||||
fn(files[path], path, metal)
|
||||
}
|
||||
done()
|
||||
}
|
||||
}
|
44
docs/lib/extract-packages-json.js
Normal file
@ -0,0 +1,44 @@
|
||||
module.exports = {extractPackages, writePackagesJSON}
|
||||
|
||||
function extractPackages(options = {}) {
|
||||
return (files, metal, done) => {
|
||||
const packages = {}
|
||||
for (const key of Object.keys(files)) {
|
||||
if (key.endsWith('package.json')) {
|
||||
const file = files[key]
|
||||
const pkg = JSON.parse(String(file.contents))
|
||||
const plucked = pluck(pkg, [
|
||||
'name',
|
||||
'description',
|
||||
'version'
|
||||
])
|
||||
plucked.dependencies = pkg.dependencies
|
||||
? Object.keys(pkg.dependencies)
|
||||
: []
|
||||
packages[pkg.name] = plucked
|
||||
}
|
||||
}
|
||||
const meta = metal.metadata()
|
||||
Object.assign(meta, {packages})
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
function writePackagesJSON(options = {}) {
|
||||
const {path = 'packages.json'} = options
|
||||
return (files, metal, done) => {
|
||||
const {packages} = metal.metadata()
|
||||
files[path] = {
|
||||
contents: JSON.stringify(packages, null, 2)
|
||||
}
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
function pluck(obj, keys) {
|
||||
const plucked = {}
|
||||
for (const key of keys) {
|
||||
plucked[key] = obj[key]
|
||||
}
|
||||
return plucked
|
||||
}
|
10
docs/lib/filter-by.js
Normal file
@ -0,0 +1,10 @@
|
||||
module.exports = function filterBy(fn) {
|
||||
return (files, metal, done) => {
|
||||
for (const [key, file] of Object.entries(files)) {
|
||||
if (!fn(file, key, metal)) {
|
||||
delete files[key]
|
||||
}
|
||||
}
|
||||
done()
|
||||
}
|
||||
}
|
65
docs/lib/ignore.js
Normal file
@ -0,0 +1,65 @@
|
||||
const {join} = require('path')
|
||||
const {existsSync, readFileSync, removeSync, writeFileSync} = require('fs')
|
||||
|
||||
module.exports = function gitIgnore(options = {}) {
|
||||
const {header, log = noop} = options
|
||||
if (!header) {
|
||||
throw new Error(`getIgnore(): the "header" is required (got: ${JSON.stringify(header)})`)
|
||||
}
|
||||
const set = new Set()
|
||||
return (files, metal, done) => {
|
||||
const ignoreFile = join(metal.destination(), '.gitignore')
|
||||
// first, get the list of previously ignored files and remove them (sync)
|
||||
const ignored = getIgnored(ignoreFile, header, log)
|
||||
for (const file of ignored) {
|
||||
if (existsSync(file)) {
|
||||
removeSync(file)
|
||||
}
|
||||
}
|
||||
for (const file of Object.keys(files)) {
|
||||
set.add(file)
|
||||
}
|
||||
setIgnored(ignoreFile, Array.from(set), header, log)
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
function readLines(file, log = noop) {
|
||||
let content
|
||||
try {
|
||||
content = readFileSync(file, 'utf8')
|
||||
} catch (error) {
|
||||
log(`ignore file ${file} does not exist!`)
|
||||
return []
|
||||
}
|
||||
|
||||
return content
|
||||
.trim()
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
}
|
||||
|
||||
function getIgnored(file, header, log = noop) {
|
||||
const lines = readLines(file)
|
||||
const headerIndex = lines.indexOf(header)
|
||||
if (headerIndex === -1) {
|
||||
log(`ignore file ${file} does not contain the automatically generated header`)
|
||||
return []
|
||||
}
|
||||
|
||||
return lines.slice(headerIndex + 1).filter(line => line)
|
||||
}
|
||||
|
||||
function setIgnored(file, files, header, log) {
|
||||
const lines = readLines(file, log)
|
||||
const headerIndex = lines.indexOf(header)
|
||||
if (headerIndex === -1) {
|
||||
lines.push(header)
|
||||
} else {
|
||||
lines.splice(headerIndex + 1)
|
||||
}
|
||||
lines.push(...files)
|
||||
writeFileSync(file, `${lines.sort().join('\n')}\n`, 'utf8')
|
||||
}
|
||||
|
||||
function noop() {}
|
49
docs/lib/mdx-loader.js
Normal file
@ -0,0 +1,49 @@
|
||||
const {getOptions} = require('loader-utils')
|
||||
const mdx = require('@mdx-js/mdx')
|
||||
|
||||
const emoji = require('remark-emoji')
|
||||
const images = require('remark-images')
|
||||
const rehypePrism = require('./rehype-prism')
|
||||
const textr = require('remark-textr')
|
||||
const toc = require('remark-toc')
|
||||
|
||||
const mdxExportJSONByDefault = require('mdx-constant')
|
||||
const grayMatter = require('gray-matter')
|
||||
const typographicBase = require('typographic-base')
|
||||
|
||||
module.exports = async function(source) {
|
||||
let result
|
||||
const {data, content: mdxContent} = grayMatter(source)
|
||||
const callback = this.async()
|
||||
|
||||
const options = Object.assign(
|
||||
{
|
||||
mdPlugins: [
|
||||
[toc, {heading: '(table of|section)? contents'}],
|
||||
images,
|
||||
emoji,
|
||||
[textr, {plugins: [typographicBase]}]
|
||||
],
|
||||
hastPlugins: [
|
||||
rehypePrism
|
||||
],
|
||||
compilers: [mdxExportJSONByDefault('frontMatter', data)]
|
||||
},
|
||||
getOptions(this),
|
||||
{filepath: this.resourcePath}
|
||||
)
|
||||
|
||||
try {
|
||||
result = await mdx(mdxContent, options)
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
let code = `
|
||||
import React from 'react'
|
||||
import {MDXTag} from '@mdx-js/tag'
|
||||
${result}
|
||||
`
|
||||
|
||||
return callback(null, code)
|
||||
}
|
31
docs/lib/parse-doc-comments.js
Normal file
@ -0,0 +1,31 @@
|
||||
const each = require('./each')
|
||||
const START = /<!-- *%docs *\n/
|
||||
const SPLIT = /\n *-->/
|
||||
const END = /<!-- *%enddocs *-->/
|
||||
|
||||
module.exports = function parseDocComments({log = noop}) {
|
||||
return each((file, path) => {
|
||||
const str = String(file.contents)
|
||||
|
||||
let parts = str.split(START)
|
||||
if (parts.length > 1) {
|
||||
// metadata should either be in frontmatter _or_ %docs comment;
|
||||
// it's too tricky to reconcile them here
|
||||
if (str.indexOf('---') === 0) {
|
||||
log('unable to parse doc comments from', path, '(existing frontmatter)')
|
||||
return
|
||||
}
|
||||
|
||||
// take the part between the start and end
|
||||
const middle = parts[1].split(END)[0]
|
||||
// split *that* on the split "marker"
|
||||
parts = middle.split(SPLIT)
|
||||
// the part before that is the "frontmatter"
|
||||
// and everything after that is the actual docs
|
||||
const [meta, docs] = parts
|
||||
file.contents = Buffer.from(`---\n${meta}\n---\n${docs}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function noop() {}
|
62
docs/lib/rehype-prism.js
Normal file
@ -0,0 +1,62 @@
|
||||
const visit = require('unist-util-visit')
|
||||
const nodeToString = require('hast-util-to-string')
|
||||
const nodeToHTML = require('hast-util-to-html')
|
||||
const refractor = require('refractor')
|
||||
|
||||
const aliases = {
|
||||
js: 'jsx',
|
||||
html: 'markup'
|
||||
}
|
||||
|
||||
module.exports = options => {
|
||||
options = options || {}
|
||||
|
||||
return tree => visit(tree, 'element', visitor)
|
||||
|
||||
function visitor(node, index, parent) {
|
||||
if (!parent || parent.tagName !== 'pre' || node.tagName !== 'code') {
|
||||
return
|
||||
}
|
||||
|
||||
const lang = getLanguage(node, options.aliases || aliases)
|
||||
|
||||
if (lang === null) {
|
||||
return
|
||||
}
|
||||
|
||||
let result = node
|
||||
const source = nodeToString(node)
|
||||
try {
|
||||
parent.properties.className = (parent.properties.className || []).concat('language-' + lang)
|
||||
result = refractor.highlight(source, lang)
|
||||
} catch (err) {
|
||||
if (/Unknown language/.test(err.message)) {
|
||||
return
|
||||
}
|
||||
throw err
|
||||
}
|
||||
|
||||
node.children = []
|
||||
node.properties.source = source
|
||||
node.properties.dangerouslySetInnerHTML = {
|
||||
__html: nodeToHTML({
|
||||
type: 'root',
|
||||
children: result
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getLanguage(node, aliases) {
|
||||
const className = node.properties.className || []
|
||||
|
||||
for (const classListItem of className) {
|
||||
if (classListItem.slice(0, 9) === 'language-') {
|
||||
let language = classListItem.slice(9).replace(/{.*/, '')
|
||||
let alias = aliases[language]
|
||||
return alias || language
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
22
docs/lib/rename.js
Normal file
@ -0,0 +1,22 @@
|
||||
module.exports = function rename(fn, options = {}) {
|
||||
const {log = noop} = options
|
||||
return (files, metal, done) => {
|
||||
for (const [key, file] of Object.entries(files)) {
|
||||
let dest = fn(file, key, files, metal)
|
||||
if (dest === true) {
|
||||
log(`[rename] keep: ${key}`)
|
||||
} else if (dest && dest !== key) {
|
||||
log(`[rename] ${key} -> ${dest}`)
|
||||
file.path = dest
|
||||
files[dest] = file
|
||||
delete files[key]
|
||||
} else if (dest === false) {
|
||||
log(`[rename] delete ${key}`)
|
||||
delete files[key]
|
||||
}
|
||||
}
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
function noop() {}
|
81
docs/lib/sync.js
Normal file
@ -0,0 +1,81 @@
|
||||
const Metalsmith = require('metalsmith')
|
||||
const filter = require('metalsmith-filter')
|
||||
const frontmatter = require('metalsmith-matters')
|
||||
const watch = require('metalsmith-watch')
|
||||
|
||||
const addPackageMeta = require('./add-package-meta')
|
||||
const {extractPackages, writePackagesJSON} = require('./extract-packages-json')
|
||||
const addSource = require('./add-source')
|
||||
const filterBy = require('./filter-by')
|
||||
const parseDocComments = require('./parse-doc-comments')
|
||||
const rename = require('./rename')
|
||||
const writeMeta = require('./write-meta')
|
||||
const gitIgnore = require('./ignore')
|
||||
|
||||
module.exports = function sync(options = {}) {
|
||||
// eslint-disable-next-line no-console
|
||||
const {log = console.warn} = options
|
||||
|
||||
const metaOptions = options.meta || {namespace: 'data', log}
|
||||
const ns = metaOptions.namespace
|
||||
|
||||
// this is what we'll resolve our Promise with later
|
||||
let files
|
||||
|
||||
const metal = Metalsmith(process.cwd())
|
||||
.source('../modules')
|
||||
.destination('pages/css')
|
||||
.clean(false)
|
||||
.frontmatter(false)
|
||||
// ignore anything containing "node_modules" in its path
|
||||
.ignore(path => path.includes('node_modules'))
|
||||
// only match files that look like docs
|
||||
.use(filter(['*/README.md', '*/docs/*.md', '*/package.json']))
|
||||
.use(extractPackages())
|
||||
// convert <!-- %docs -->...<!-- %enddocs --> blocks into frontmatter
|
||||
.use(parseDocComments({log}))
|
||||
// parse frontmatter into "data" key of each file
|
||||
.use(frontmatter(metaOptions))
|
||||
// only match files that have a "path" key in their frontmatter
|
||||
.use(filterBy(file => file[ns].path))
|
||||
.use(writePackagesJSON({path: 'packages.json'}))
|
||||
// write the source frontmatter key to the relative source path
|
||||
.use(
|
||||
addSource({
|
||||
branch: 'master',
|
||||
repo: 'primer/primer',
|
||||
log
|
||||
})
|
||||
)
|
||||
// copy a subset of fields from the nearest package.json
|
||||
.use(
|
||||
addPackageMeta({
|
||||
fields: ['name', 'description', 'version'],
|
||||
namespace: ns
|
||||
})
|
||||
)
|
||||
// rename files with their "path" frontmatter key
|
||||
.use(rename(file => file[ns] ? `${file[ns].path}.md` : true), {log})
|
||||
.use((_files, metal, done) => {
|
||||
files = _files
|
||||
done()
|
||||
})
|
||||
// write frontmatter back out to the file
|
||||
.use(writeMeta(metaOptions))
|
||||
// keep .gitignore up-to-date with the list of generated files
|
||||
.use(
|
||||
gitIgnore({
|
||||
header: '# DO NOT EDIT: automatically generated by ignore.js'
|
||||
})
|
||||
)
|
||||
|
||||
if (options.watch) {
|
||||
metal.use(watch(typeof options.watch === 'object' ? options.watch : {}))
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
metal.build(error => {
|
||||
error ? reject(error) : resolve(files)
|
||||
})
|
||||
})
|
||||
}
|
11
docs/lib/write-meta.js
Normal file
@ -0,0 +1,11 @@
|
||||
const matter = require('gray-matter')
|
||||
const each = require('./each')
|
||||
|
||||
module.exports = function writeMeta({namespace = 'data'}) {
|
||||
return each(file => {
|
||||
const data = file[namespace]
|
||||
if (data) {
|
||||
file.contents = matter.stringify(String(file.contents), data)
|
||||
}
|
||||
})
|
||||
}
|
14
docs/next.config.js
Normal file
@ -0,0 +1,14 @@
|
||||
const {join, resolve} = require('path')
|
||||
const withSass = require('@zeit/next-sass')
|
||||
const configure = require('./lib/config')
|
||||
|
||||
module.exports = configure(
|
||||
withSass({
|
||||
sassLoaderOptions: {
|
||||
includePaths: [
|
||||
resolve(__dirname, '../modules'),
|
||||
resolve(__dirname, 'node_modules')
|
||||
]
|
||||
}
|
||||
})
|
||||
)
|
23
docs/now.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"version": 1,
|
||||
"name": "primer-css",
|
||||
"alias": "primer-css.now.sh",
|
||||
"scale": {
|
||||
"sfo": {
|
||||
"min": 1,
|
||||
"max": 3
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": "8"
|
||||
},
|
||||
"files": [
|
||||
"copy.js",
|
||||
"lib",
|
||||
"next.config.js",
|
||||
"package-lock.json",
|
||||
"pages",
|
||||
"src",
|
||||
"static"
|
||||
]
|
||||
}
|
12781
docs/package-lock.json
generated
Normal file
73
docs/package.json
Normal file
@ -0,0 +1,73 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@primer/css-docs",
|
||||
"scripts": {
|
||||
"lint": "eslint pages src",
|
||||
"pretest": "npm run sync",
|
||||
"test": "node test/urls.js",
|
||||
"check-links": "script/check-links http://localhost:3000/css -v",
|
||||
"sync": "script/sync",
|
||||
"watch": "script/sync --watch",
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@githubprimer/octicons-react": "^8.1.3",
|
||||
"@mdx-js/mdx": "^0.16.6",
|
||||
"@mdx-js/tag": "0.15.0",
|
||||
"@primer/components": "^8.2.0-beta",
|
||||
"@svgr/webpack": "2.4.1",
|
||||
"@zeit/next-css": "^1.0.1",
|
||||
"@zeit/next-sass": "^1.0.1",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"broken-link-checker": "^0.7.8",
|
||||
"char-spinner": "^1.0.1",
|
||||
"chroma-js": "^1.4.1",
|
||||
"clipboard-copy-element": "^0.5.0",
|
||||
"colorette": "^1.0.7",
|
||||
"details-dialog-element": "^1.4.0",
|
||||
"eslint": "4.19.1",
|
||||
"eslint-plugin-github": "1.0.0",
|
||||
"eslint-plugin-prettier": "^3.0.0",
|
||||
"fs-extra": "^4.0.2",
|
||||
"gray-matter": "^4.0.1",
|
||||
"hast-util-to-html": "^5.0.0",
|
||||
"hast-util-to-string": "^1.0.1",
|
||||
"html-2-jsx": "^0.5.1-dev",
|
||||
"klaw": "^3.0.0",
|
||||
"loader-utils": "^1.1.0",
|
||||
"mdx-constant": "^0.1.0",
|
||||
"mdx-live": "2.0.0-alpha.2",
|
||||
"metalsmith": "^2.3.0",
|
||||
"metalsmith-filter": "^1.0.2",
|
||||
"metalsmith-matters": "^1.2.0",
|
||||
"metalsmith-watch": "^1.0.3",
|
||||
"minimatch": "^3.0.4",
|
||||
"next": "7.0.2",
|
||||
"next-compose-plugins": "2.1.1",
|
||||
"node-sass": "^4.10.0",
|
||||
"now": "^12.1.8",
|
||||
"primer": "10.10.5",
|
||||
"primer-colors": "^1.0.1",
|
||||
"prism-github": "^1.1.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "16.6.1",
|
||||
"react-dom": "16.6.1",
|
||||
"react-measure": "^2.2.2",
|
||||
"refractor": "^2.6.2",
|
||||
"remark-emoji": "^2.0.2",
|
||||
"remark-images": "^0.16.1",
|
||||
"remark-parse": "^6.0.3",
|
||||
"remark-textr": "^3.0.3",
|
||||
"remark-toc": "^5.1.1",
|
||||
"styled-components": "^4.1.2",
|
||||
"title-case": "^2.1.1",
|
||||
"tree-model": "^1.0.7",
|
||||
"typographic-base": "^1.0.4",
|
||||
"unified": "^7.1.0",
|
||||
"unist-util-select": "^2.0.0",
|
||||
"unist-util-stringify-position": "^2.0.0",
|
||||
"unist-util-visit": "^1.4.0"
|
||||
}
|
||||
}
|
83
docs/pages/_app.js
Normal file
@ -0,0 +1,83 @@
|
||||
import React from 'react'
|
||||
import App, {Container} from 'next/app'
|
||||
import {MDXProvider} from '@mdx-js/tag'
|
||||
import Head from 'next/head'
|
||||
import {BaseStyles, BorderBox, Box, Flex, theme} from '@primer/components'
|
||||
import {Header, PackageHeader, SideNav} from '../src/components'
|
||||
import getComponents from '../src/markdown'
|
||||
import {config, requirePage, rootPage} from '../src/utils'
|
||||
import {CONTENT_MAX_WIDTH} from '../src/constants'
|
||||
|
||||
import 'primer/index.scss'
|
||||
|
||||
export default class MyApp extends App {
|
||||
static async getInitialProps({Component, ctx}) {
|
||||
let page = {}
|
||||
|
||||
if (Component.getInitialProps) {
|
||||
page = await Component.getInitialProps(ctx)
|
||||
}
|
||||
|
||||
return {page}
|
||||
}
|
||||
|
||||
render() {
|
||||
// strip the trailing slash
|
||||
const pathname = this.props.router.pathname.replace(/\/$/, '')
|
||||
const {Component, page} = this.props
|
||||
|
||||
const node = rootPage.first(node => node.path === pathname) || {}
|
||||
const {file, meta = {}} = node || {}
|
||||
const components = getComponents(node)
|
||||
|
||||
const Hero = file ? requirePage(file).Hero : null
|
||||
|
||||
return (
|
||||
<BaseStyles fontSize={2} style={{fontFamily: theme.fonts.normal}}>
|
||||
<Container>
|
||||
<Head>
|
||||
<title>Primer CSS{meta.title ? ` / ${meta.title}` : null}</title>
|
||||
</Head>
|
||||
<Header />
|
||||
<Flex
|
||||
flexDirection={['column', 'column', 'column', 'row-reverse']}
|
||||
alignContent="stretch"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Box width={['auto', 'auto', '100%']}>
|
||||
{Hero ? <Hero /> : null}
|
||||
<Box color="gray.9" maxWidth={['auto', 'auto', 'auto', CONTENT_MAX_WIDTH]} px={6} mx="auto" my={6}>
|
||||
<div className="markdown-body">
|
||||
{!meta.hero && meta.title ? <h1>{meta.title}</h1> : null}
|
||||
<PackageHeader {...meta} />
|
||||
<MDXProvider components={components}>
|
||||
<Component {...page} />
|
||||
</MDXProvider>
|
||||
{config.production ? null : (
|
||||
<details>
|
||||
<summary>Metadata</summary>
|
||||
<pre>{JSON.stringify(meta, null, 2)}</pre>
|
||||
</details>
|
||||
)}
|
||||
</div>
|
||||
{/* TODO: bring back edit link! */}
|
||||
</Box>
|
||||
</Box>
|
||||
<BorderBox
|
||||
width={['100%', '100%', 256]}
|
||||
minWidth={256}
|
||||
bg="gray.0"
|
||||
borderColor="gray.2"
|
||||
borderRadius={0}
|
||||
border={0}
|
||||
borderRight={1}
|
||||
borderTop={[1, 1, 0, 0]}
|
||||
>
|
||||
<SideNav />
|
||||
</BorderBox>
|
||||
</Flex>
|
||||
</Container>
|
||||
</BaseStyles>
|
||||
)
|
||||
}
|
||||
}
|
51
docs/pages/_document.js
Normal file
@ -0,0 +1,51 @@
|
||||
import React from 'react'
|
||||
import Document, {Head, Main, NextScript} from 'next/document'
|
||||
import {ServerStyleSheet} from 'styled-components'
|
||||
import {getAssetPath, CommonStyles, CommonScripts} from '../src/utils'
|
||||
|
||||
export default class MyDocument extends Document {
|
||||
static getInitialProps({renderPage}) {
|
||||
const sheet = new ServerStyleSheet()
|
||||
const page = renderPage(App => props => sheet.collectStyles(<App {...props} />))
|
||||
return {
|
||||
...page,
|
||||
renderedStyles: sheet.getStyleElement()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {files, renderedStyles} = this.props
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<Head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-126681523-2" />
|
||||
<script async href={getAssetPath('analytics.js')} />
|
||||
<meta charSet="utf8" />
|
||||
<link rel="icon" href={getAssetPath('favicon.png')} />
|
||||
<link rel="apple-touch-icon" href={getAssetPath('apple-touch-icon.png')} />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<meta property="og:title" content="Primer CSS" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://primer.style/css/" />
|
||||
<meta property="og:description" content="CSS for GitHub's Primer design system" />
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://user-images.githubusercontent.com/586552/47590375-121cad80-d93a-11e8-89f2-a1cf108e0548.png"
|
||||
/>
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:site" content="@githubprimer" />
|
||||
<CommonStyles />
|
||||
{renderedStyles}
|
||||
</Head>
|
||||
<body data-files={JSON.stringify(files)}>
|
||||
<Main />
|
||||
<NextScript />
|
||||
<CommonScripts />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
}
|
43
docs/pages/css/.gitignore
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# DO NOT EDIT: automatically generated by ignore.js
|
||||
components/alerts.md
|
||||
components/avatars.md
|
||||
components/blankslate.md
|
||||
components/box.md
|
||||
components/branch-name.md
|
||||
components/breadcrumb.md
|
||||
components/buttons.md
|
||||
components/forms.md
|
||||
components/labels.md
|
||||
components/markdown.md
|
||||
components/marketing-buttons.md
|
||||
components/navigation.md
|
||||
components/pagination.md
|
||||
components/popover.md
|
||||
components/progress.md
|
||||
components/subhead.md
|
||||
components/tooltips.md
|
||||
components/truncate.md
|
||||
objects/grid.md
|
||||
objects/layout.md
|
||||
objects/table-object.md
|
||||
packages.json
|
||||
support/breakpoints.md
|
||||
support/index.md
|
||||
support/marketing-variables.md
|
||||
support/spacing.md
|
||||
support/typography.md
|
||||
utilities/animations.md
|
||||
utilities/borders.md
|
||||
utilities/box-shadow.md
|
||||
utilities/details.md
|
||||
utilities/flexbox.md
|
||||
utilities/layout.md
|
||||
utilities/margin.md
|
||||
utilities/marketing-borders.md
|
||||
utilities/marketing-filters.md
|
||||
utilities/marketing-layout.md
|
||||
utilities/marketing-margin.md
|
||||
utilities/marketing-padding.md
|
||||
utilities/marketing-type.md
|
||||
utilities/padding.md
|
||||
utilities/typography.md
|
76
docs/pages/css/components/box-overlay.md
Normal file
@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Box overlay
|
||||
status: Experimental
|
||||
status_issue: 'https://github.com/github/design-systems/issues/374'
|
||||
source: 'https://github.com/github/github/tree/master/app/assets/stylesheets/components/box-overlay.scss'
|
||||
symbols: [Box--overlay, Box-header, Box-overlay--narrow, Box-overlay--wide]
|
||||
keywords: [box, overlay]
|
||||
---
|
||||
|
||||
import DetailsDialog from '../../../src/DetailsDialog'
|
||||
|
||||
## Box overlays
|
||||
Use the `Box--overlay` with the `<details>` and [`<details-dialog>`](https://github.com/github/details-dialog), and add the `details-overlay-dark` utility if you wish to apply a dark transparent background.
|
||||
|
||||
Box overlays come in three widths. The default `Box--overlay` is 440px wide, `Box-overlay--narrow` is 320px wide, and `Box-overlay--wide` is 640px wide.
|
||||
|
||||
**Note:** `position: fixed` has been disabled in this example
|
||||
|
||||
```erb
|
||||
<details class="details-reset details-overlay details-overlay-dark">
|
||||
<summary class="btn" aria-haspopup="dialog">Open dialog</summary>
|
||||
<details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast">
|
||||
<div class="Box-header">
|
||||
<button class="Box-btn-octicon btn-octicon float-right" type="button" aria-label="Close dialog" data-close-dialog>
|
||||
<%= octicon "x" %>
|
||||
</button>
|
||||
<h3 class="Box-title">Box title</h3>
|
||||
</div>
|
||||
<div class="overflow-auto">
|
||||
<div class="Box-body overflow-auto">
|
||||
<p>
|
||||
The quick brown fox jumps over the lazy dog and feels as if he were in the seventh heaven of typography together with Hermann Zapf, the most famous artist of the...
|
||||
</p>
|
||||
</div>
|
||||
<ul>
|
||||
<li class="Box-row">
|
||||
<img class="avatar v-align-middle mr-2" src="https://avatars.githubusercontent.com/broccolini?s=48" alt="broccolini" width="24" height="24">
|
||||
@broccolini
|
||||
</li>
|
||||
<li class="Box-row border-bottom">
|
||||
<img class="avatar v-align-middle mr-2" src="https://avatars.githubusercontent.com/jonrohan?s=48" alt="jonrohan" width="24" height="24">
|
||||
@jonrohan
|
||||
</li>
|
||||
<li class="Box-row border-bottom">
|
||||
<img class="avatar v-align-middle mr-2" src="https://avatars.githubusercontent.com/shawnbot?s=48" alt="shawnbot" width="24" height="24">
|
||||
@shawnbot
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="Box-footer">
|
||||
<button type="button" class="btn btn-block" data-close-dialog>Okidoki</button>
|
||||
</div>
|
||||
</details-dialog>
|
||||
</details>
|
||||
```
|
||||
|
||||
In github.com there is a shared dialog partial. You will only have to pass in the modal content:
|
||||
|
||||
```erb
|
||||
<%= render layout: "shared/details_dialog", locals: {
|
||||
button_text: "Delete account",
|
||||
title: "Are you sure you want to delete this account?",
|
||||
button_class: "btn btn-danger"
|
||||
} do %>
|
||||
<div class="Box-body overflow-auto">
|
||||
<p>
|
||||
This action is irreversible.
|
||||
</p>
|
||||
<button type="button" class="btn btn-block btn-danger mt-2" data-close-dialog>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
```
|
||||
|
||||
[ARIA attributes]: https://www.w3.org/TR/html-aria/#allowed-aria-roles-states-and-properties
|
8
docs/pages/css/components/boxed-groups.md
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
title: Boxed groups
|
||||
status: Deprecated
|
||||
source: 'https://github.com/github/github/blob/master/app/assets/stylesheets/components/boxed-groups.scss'
|
||||
symbols: [BtnGroup, Counter, ajax-error-message, approved, avatar, bleed-flush, boxed-action, boxed-group, boxed-group-action, boxed-group-breadcrumb, boxed-group-inner, boxed-group-list, boxed-group-standalone, boxed-group-table, boxed-group-warning, btn-sm, compact, compact-options, condensed, dangerzone, dashboard-sidebar, field-with-errors, flush, heading, help, inline-error, markdown-body, octicon, octicon-alert, one-half, rejected, seamless, selected, spinner, standalone, tabnav, tabnav-tab, visible]
|
||||
---
|
||||
|
||||
The `boxed-group` styles have been deprecated. Please use the [Box component](/css/components/box) instead.
|
187
docs/pages/css/components/dropdown.md
Normal file
@ -0,0 +1,187 @@
|
||||
---
|
||||
title: Dropdown
|
||||
status: Stable
|
||||
source: 'https://github.com/github/github/tree/master/app/assets/stylesheets/components/dropdown.scss'
|
||||
symbols: [active, anim-scale-in, btn-link, dropdown, dropdown-caret, dropdown-divider, dropdown-header, dropdown-item, dropdown-menu, dropdown-menu-content, dropdown-menu-e, dropdown-menu-ne, dropdown-menu-no-overflow, dropdown-menu-s, dropdown-menu-se, dropdown-menu-sw, dropdown-menu-w, dropdown-signout, octicon, zeroclipboard-is-hover]
|
||||
---
|
||||
|
||||
Dropdowns are lightweight, JavaScript-powered context menus for housing navigation and actions. They're great for instances where you don't need the full power (and code) of the select menu.
|
||||
|
||||
{:toc}
|
||||
|
||||
## Basic examples
|
||||
|
||||
Dropdowns should be trigged by a `<button>`. **[Each dropdown menu requires a directional class](#alignment)**, much like our tooltips.
|
||||
|
||||
Using a GitHub button:
|
||||
|
||||
```html
|
||||
<details class="dropdown details-reset details-overlay d-inline-block">
|
||||
<summary class="btn" aria-haspopup="true">
|
||||
Dropdown
|
||||
<div class="dropdown-caret"></div>
|
||||
</summary>
|
||||
|
||||
<ul class="dropdown-menu dropdown-menu-se">
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
```
|
||||
|
||||
Using a button customized with additional utilities:
|
||||
|
||||
|
||||
```html
|
||||
<details class="dropdown details-reset details-overlay d-inline-block">
|
||||
<summary class="text-gray p-2 d-inline" aria-haspopup="true">
|
||||
Dropdown
|
||||
<div class="dropdown-caret"></div>
|
||||
</summary>
|
||||
|
||||
<ul class="dropdown-menu dropdown-menu-se">
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
### Alignment
|
||||
|
||||
Align the direction of dropdown menus and their arrows with modifier classes.
|
||||
|
||||
```html
|
||||
<details class="dropdown details-reset details-overlay d-inline-block">
|
||||
<summary class="btn" aria-haspopup="true">
|
||||
.dropdown-ne
|
||||
<div class="dropdown-caret"></div>
|
||||
</summary>
|
||||
|
||||
<ul class="dropdown-menu dropdown-menu-ne">
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
```
|
||||
|
||||
```html
|
||||
<details class="dropdown details-reset details-overlay d-inline-block">
|
||||
<summary class="btn" aria-haspopup="true">
|
||||
.dropdown-e
|
||||
<div class="dropdown-caret"></div>
|
||||
</summary>
|
||||
|
||||
<ul class="dropdown-menu dropdown-menu-e">
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
```
|
||||
|
||||
```html
|
||||
<details class="dropdown details-reset details-overlay d-inline-block">
|
||||
<summary class="btn" aria-haspopup="true">
|
||||
.dropdown-se
|
||||
<div class="dropdown-caret"></div>
|
||||
</summary>
|
||||
|
||||
<ul class="dropdown-menu dropdown-menu-se">
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
```
|
||||
|
||||
```html
|
||||
<details class="dropdown details-reset details-overlay d-inline-block">
|
||||
<summary class="btn" aria-haspopup="true">
|
||||
.dropdown-s
|
||||
<div class="dropdown-caret"></div>
|
||||
</summary>
|
||||
|
||||
<ul class="dropdown-menu dropdown-menu-s">
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
```
|
||||
|
||||
```html
|
||||
<details class="dropdown details-reset details-overlay d-inline-block">
|
||||
<summary class="btn" aria-haspopup="true">
|
||||
.dropdown-sw
|
||||
<div class="dropdown-caret"></div>
|
||||
</summary>
|
||||
|
||||
<ul class="dropdown-menu dropdown-menu-sw">
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
```
|
||||
|
||||
```html
|
||||
<details class="dropdown details-reset details-overlay d-inline-block">
|
||||
<summary class="btn" aria-haspopup="true">
|
||||
.dropdown-w
|
||||
<div class="dropdown-caret"></div>
|
||||
</summary>
|
||||
|
||||
<ul class="dropdown-menu dropdown-menu-w">
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
```
|
||||
|
||||
### Dividers
|
||||
|
||||
```html
|
||||
<details class="dropdown details-reset details-overlay d-inline-block">
|
||||
<summary class="btn" aria-haspopup="true">
|
||||
Dropdown
|
||||
<div class="dropdown-caret"></div>
|
||||
</summary>
|
||||
|
||||
<ul class="dropdown-menu dropdown-menu-se">
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li class="dropdown-divider" role="separator"></li>
|
||||
<li><a class="dropdown-item" href="#url">Another item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">One more</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
```
|
||||
|
||||
### Headers
|
||||
|
||||
```html
|
||||
<details class="dropdown details-reset details-overlay d-inline-block">
|
||||
<summary class="btn" aria-haspopup="true">
|
||||
Dropdown
|
||||
<div class="dropdown-caret"></div>
|
||||
</summary>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-se">
|
||||
<div class="dropdown-header">
|
||||
Dropdown header
|
||||
</div>
|
||||
<ul>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
<li><a class="dropdown-item" href="#url">Dropdown item</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
```
|
11
docs/pages/css/components/flash-banner.md
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
title: Flash banner
|
||||
status: In review
|
||||
status_issue: 'https://github.com/github/design-systems/issues/99'
|
||||
source: 'https://github.com/github/github/tree/master/app/assets/stylesheets/components/flash-banner.scss'
|
||||
symbols: [flash-banner, is-signed-in, is-signed-out, org-privileges-tour-dismiss, signed-in-tab-flash, signed-out-tab-flash, stale-session-flash]
|
||||
---
|
||||
|
||||
**Needs documentation.**
|
||||
|
||||
A banner rendered at the top of the page and a warning banner shown when you log in/out in another tab.
|
8
docs/pages/css/components/index.md
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
title: Components
|
||||
---
|
||||
|
||||
Components make it easier to mark up a set of elements that are commonly grouped together with similar visual styles. Their base styles are shared and variants are added with modifier classes, so usually components are comprised of multiple classes. Most importantly, components should be highly reusable across the site, rather than built for specific pages or features. To achieve this:
|
||||
|
||||
* **Separate structure and skin:** This means to define repeating visual features (like background and border styles) as separate “skins” that you can mix-and-match with your various components to achieve a large amount of visual variety without much code.
|
||||
* **Separate container and content:** Essentially, this means “rarely use location-dependent styles”. A component should look the same no matter where you put it.
|
213
docs/pages/css/components/pagehead.md
Normal file
@ -0,0 +1,213 @@
|
||||
---
|
||||
title: Pagehead
|
||||
status: Stable
|
||||
internal: true
|
||||
source: 'https://github.com/github/github/tree/master/app/assets/stylesheets/components/pagehead.scss'
|
||||
symbols: [account-switcher, active, admin, avatar, dropdown-menu-content, experiment-repo-nav, fork, fork-flag, mirror, mirror-flag, octicon, octicon-lock, octicon-mirror, octicon-mute, pagehead, pagehead-actions, pagehead-heading, pagehead-tabs-item, path-divider, png, private, repohead, repohead-details-container, reponav, reponav-dropdown, reponav-item, select-menu, select-menu-modal-holder, selected, underline-nav]
|
||||
---
|
||||
|
||||
Give a page a clear, separated title and optional top nav by adding a `.pagehead`.
|
||||
|
||||
{:toc}
|
||||
|
||||
## Basic pagehead
|
||||
|
||||
In its simplest form, you can build a pagehead with a `pagehead` element with an `h1` child.
|
||||
|
||||
```html
|
||||
<div class="pagehead">
|
||||
<h1>Title</h1>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Avatars
|
||||
|
||||
Place an avatar inside the `h1` to have margins applied to it. Don't forget the `avatar` class!
|
||||
|
||||
Also be sure to add `aria-hidden="true"` to hide the avatar from screenreaders.
|
||||
|
||||
```erb
|
||||
<div class="pagehead">
|
||||
<h1>
|
||||
<%= avatar_for("jonrohan", 32, "aria-hidden": "true") %>
|
||||
jonrohan
|
||||
</h1>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Path divider
|
||||
|
||||
For path or breadcrumb patterns, wrap the `/` with `path-divider` to add some nice horizontal spacing.
|
||||
|
||||
```html
|
||||
<div class="pagehead">
|
||||
<h1>
|
||||
<span class="author"><a href="#url" class="url fn" rel="author">jonrohan</a></span>
|
||||
<span class="path-divider">/</span>
|
||||
<strong><a href="#url">primer</a></strong>
|
||||
</h1>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Pagehead actions
|
||||
|
||||
To add actions on the right side of the `pagehead`, use the `pagehead-actions` element. Place `pagehead-actions` before the `h1` to ensure proper placement.
|
||||
|
||||
```html
|
||||
<div class="pagehead">
|
||||
<ul class="pagehead-actions">
|
||||
<li><button class="btn btn-sm" href="#url">Action</button></li>
|
||||
<li><button class="btn btn-sm btn-primary" href="#url">Action</button></li>
|
||||
</ul>
|
||||
<h1>Title</h1>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Org nav
|
||||
|
||||
```html
|
||||
<div class="pagehead orghead px-3">
|
||||
<nav class="orgnav clearfix" role="navigation">
|
||||
<a class="pagehead-tabs-item selected" href="#url">
|
||||
<%= octicon("repo") %>
|
||||
Repositories
|
||||
</a>
|
||||
<a class="pagehead-tabs-item" href="#url">
|
||||
<%= octicon("jersey") %>
|
||||
Teams
|
||||
<span class="Counter">1</span>
|
||||
</a>
|
||||
<a class="pagehead-tabs-item" href="#url">
|
||||
<%= octicon("project") %>
|
||||
Projects
|
||||
<span class="Counter">2</span>
|
||||
</a>
|
||||
<a class="pagehead-tabs-item" href="#url">
|
||||
<%= octicon("briefcase") %>
|
||||
Jobs
|
||||
<span class="Counter">3</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Repohead
|
||||
|
||||
Use `pagehead` to construct a header and navigation for a repository.
|
||||
|
||||
### Base
|
||||
|
||||
```html
|
||||
<div class="pagehead repohead experiment-repo-nav">
|
||||
<div class="container-lg repohead-details-container px-3 clearfix">
|
||||
<ul class="pagehead-actions">
|
||||
<li>
|
||||
<div class="clearfix">
|
||||
<button class="btn btn-sm btn-with-count" href="#url" role="button">
|
||||
<%= octicon("eye")%>
|
||||
Watch
|
||||
</button>
|
||||
<button class="social-count" href="#url">6</button>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="clearfix">
|
||||
<button class="btn btn-sm btn-with-count" href="#url" role="button">
|
||||
<%= octicon("repo-forked") %>
|
||||
Fork
|
||||
</button>
|
||||
<button class="social-count" href="#url">0</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<h1>
|
||||
<%= octicon("repo") %>
|
||||
<span class="author"><a href="#url" class="url fn" rel="author">cmwinters</a></span>
|
||||
<span class="path-divider">/</span>
|
||||
<strong><a href="#url">project</a></strong>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="container-lg px-3">
|
||||
<nav class="reponav clearfix" role="navigation">
|
||||
<a class="reponav-item selected" href="#url">
|
||||
<%= octicon("code") %>
|
||||
Code
|
||||
</a>
|
||||
<a class="reponav-item" href="#url">
|
||||
<%= octicon("issue-opened") %>
|
||||
Issues
|
||||
<span class="Counter">1</span>
|
||||
</a>
|
||||
<a class="reponav-item" href="#url">
|
||||
<%= octicon("git-pull-request") %>
|
||||
Pull requests
|
||||
<span class="Counter">2</span>
|
||||
</a>
|
||||
<a class="reponav-item" href="#url">
|
||||
<%= octicon("project") %>
|
||||
Projects
|
||||
<span class="Counter">3</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Private
|
||||
|
||||
```html
|
||||
<div class="pagehead repohead experiment-repo-nav">
|
||||
<div class="container-lg repohead-details-container px-3 clearfix">
|
||||
<ul class="pagehead-actions">
|
||||
<li>
|
||||
<div class="clearfix">
|
||||
<button class="btn btn-sm btn-with-count" href="#url" role="button">
|
||||
<%= octicon("eye") %>
|
||||
Watch
|
||||
</button>
|
||||
<button class="social-count" href="#url">6</button>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="clearfix">
|
||||
<button class="btn btn-sm btn-with-count" href="#url" role="button">
|
||||
<%= octicon("repo-forked") %>
|
||||
Fork
|
||||
</button>
|
||||
<button class="social-count" href="#url">0</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<h1 class="private">
|
||||
<%= octicon("lock") %>
|
||||
<span class="author"><a href="#url" class="url fn" rel="author">cmwinters</a></span>
|
||||
<span class="path-divider">/</span>
|
||||
<strong><a href="#url">project</a></strong>
|
||||
<span class="Label Label--outline v-align-middle">Private</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="container-lg px-3">
|
||||
<nav class="reponav clearfix" role="navigation">
|
||||
<a class="reponav-item selected" href="#url">
|
||||
<%= octicon("code") %>
|
||||
Code
|
||||
</a>
|
||||
<a class="reponav-item" href="#url">
|
||||
<%= octicon("issue-opened") %>
|
||||
Issues
|
||||
<span class="Counter">1</span>
|
||||
</a>
|
||||
<a class="reponav-item" href="#url">
|
||||
<%= octicon("git-pull-request") %>
|
||||
Pull requests
|
||||
<span class="Counter">2</span>
|
||||
</a>
|
||||
<a class="reponav-item" href="#url">
|
||||
<%= octicon("project") %>
|
||||
Projects
|
||||
<span class="Counter">3</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
```
|
836
docs/pages/css/components/select-menu.md
Normal file
@ -0,0 +1,836 @@
|
||||
---
|
||||
title: Select menu
|
||||
status: Stable
|
||||
source: 'https://github.com/github/github/blob/master/docs/styleguide/css/styles/product/components/select-menu.md'
|
||||
symbols: [active, close-button, css-truncate-target, description, description-inline, description-warning, disabled, filterable-empty, has-error, hidden-select-button-text, icon-only, indeterminate, is-loading, is-showing-new-item-form, label-select-menu, last-visible, menu-active, modal-backdrop, navigation-focus, octicon, octicon-check, octicon-dash, octicon-octoface, octicon-x, opaque, primary, select-menu, select-menu-action, select-menu-blankslate, select-menu-button, select-menu-button-gravatar, select-menu-button-large, select-menu-clear-item, select-menu-divider, select-menu-error, select-menu-filters, select-menu-header, select-menu-item, select-menu-item-gravatar, select-menu-item-heading, select-menu-item-icon, select-menu-item-parent, select-menu-item-template, select-menu-item-text, select-menu-list, select-menu-loading-overlay, select-menu-modal, select-menu-modal-holder, select-menu-modal-narrow, select-menu-modal-right, select-menu-new-item-form, select-menu-no-results, select-menu-tab, select-menu-tab-bucket, select-menu-tab-nav, select-menu-tabs, select-menu-text-filter, select-menu-title, selected, spinner]
|
||||
---
|
||||
|
||||
The select menu provides advanced support for navigation, filtering, and more. Any popover within a select menu can make use of JavaScript-enabled live filtering, selected states, tabbed lists, and keyboard navigation with a bit of markup.
|
||||
|
||||
{:toc}
|
||||
|
||||
## Basic example
|
||||
|
||||
Select menus should be trigged by a `<button>`. In the markup below, all classes prefixed with `select-menu` and `.js-` are required.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
Choose an item
|
||||
</button>
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<a href="#url" class="select-menu-item selected js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 1</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 2</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 3</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Menu contents
|
||||
|
||||
The contents of a select menu are easily customized with support for headers, footers, tabbed lists, and live filtering.
|
||||
|
||||
### Headers
|
||||
|
||||
Add a header to any select menu's popover to house a clear title and a dismiss button.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
Choose an item
|
||||
</button>
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-header js-navigation-enable" tabindex="-1">
|
||||
<button class="btn-link close-button js-menu-close" type="button"><%= octicon("x", :"aria-label" => "Close menu") %></button>
|
||||
<span class="select-menu-title">Options</span>
|
||||
</div>
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<a href="#url" class="select-menu-item selected js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 1</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 2</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 3</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### List items
|
||||
|
||||
The list of items is arguably the most important subcomponent within the menu. Build them out of anchors, buttons, or just about any [interactive content](http://w3c.github.io/html/dom.html#interactive-content). [List items are also customizable](#menu-list-items) with options for avatars, additional icons, and multiple lines of text.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
Choose an item
|
||||
</button>
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<button type="button" class="select-menu-item selected js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 1</span>
|
||||
</button>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 2</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item" href="#url">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 3</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Filter
|
||||
|
||||
Enable live filtering of list items within a `.select-menu-list` with a search input. Only a handful of changes to your menu's markup are required:
|
||||
|
||||
- Add the text input, housed in `.select-menu-filters`, before the `.select-menu-list`.
|
||||
- Add two attritubes, `data-filterable-for` and `data-filterable-type`, to the `.select-menu-list` to scope the filter to the list.
|
||||
|
||||
There are no required changes for the `.select-menu-item`s.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
<i>Label:</i>
|
||||
<span class="js-select-button">Choose an item</span>
|
||||
</button>
|
||||
<div class="select-menu-modal-holder js-menu-content js-navigation-container">
|
||||
<div class="select-menu-modal">
|
||||
<div class="select-menu-header" tabindex="-1">
|
||||
<button class="btn-link close-button js-menu-close" type="button"><%= octicon("x", :"aria-label" => "Close menu") %></button>
|
||||
<span class="select-menu-title">Options</span>
|
||||
</div>
|
||||
<div class="js-select-menu-deferred-content">
|
||||
<div class="select-menu-filters">
|
||||
<div class="select-menu-text-filter">
|
||||
<input type="text" id="example-filter-field" class="form-control js-filterable-field js-navigation-enable" placeholder="Filter this list" aria-label="Type to filter" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<div class="select-menu-list" data-filterable-for="example-filter-field" data-filterable-type="substring">
|
||||
<input hidden="checkbox" name="example" value="">
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Test element
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Filter to this
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
More content
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Something else
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
One more thing
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Tabs
|
||||
|
||||
Sometimes you need two or more lists of items in your select menu, e.g. branches and tags. Select menu lists can be tabbed with the addition of the tab toggles at the top of the menu and a few changes to the `.select-menu-list`.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
<i>Label:</i>
|
||||
<span class="js-select-button">Choose an item</span>
|
||||
</button>
|
||||
<div class="select-menu-modal-holder js-menu-content js-navigation-container">
|
||||
<div class="select-menu-modal">
|
||||
<div class="select-menu-header" tabindex="-1">
|
||||
<button class="btn-link close-button js-menu-close" type="button"><%= octicon("x", :"aria-label" => "Close menu") %></button>
|
||||
<span class="select-menu-title">Options</span>
|
||||
</div>
|
||||
<div class="js-select-menu-deferred-content">
|
||||
<div class="select-menu-filters">
|
||||
<div class="select-menu-tabs">
|
||||
<ul>
|
||||
<li class="select-menu-tab">
|
||||
<a href="#url" data-tab-filter="branches" data-filter-placeholder="Filter for " class="js-select-menu-tab" aria-current="false">Branches</a>
|
||||
</li>
|
||||
<li class="select-menu-tab">
|
||||
<a href="#url" data-tab-filter="tags" data-filter-placeholder="Find a tag…" class="js-select-menu-tab selected" aria-current="true">Tags</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket selected" data-filterable-for="example-filter-field" data-filterable-type="substring" data-tab-filter="branches" role="menu">
|
||||
<input hidden="checkbox" name="example" value="">
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Branch 1
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Branch 2
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Branch 3
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Branch 4
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Branch 5
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket" data-filterable-for="example-filter-field" data-filterable-type="substring" data-tab-filter="tags" role="menu">
|
||||
<input hidden="checkbox" name="example" value="">
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Tag 1
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Tag 2
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Tag 3
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Tag 4
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Tag 5
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Filter and tabs
|
||||
|
||||
Show a filter and tabs in a single select menu.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
<i>Label:</i>
|
||||
<span class="js-select-button">Choose an item</span>
|
||||
</button>
|
||||
<div class="select-menu-modal-holder js-menu-content js-navigation-container">
|
||||
<div class="select-menu-modal">
|
||||
<div class="select-menu-header" tabindex="-1">
|
||||
<button class="btn-link close-button js-menu-close" type="button"><%= octicon("x", :"aria-label" => "Close menu") %></button>
|
||||
<span class="select-menu-title">Options</span>
|
||||
</div>
|
||||
<div class="js-select-menu-deferred-content">
|
||||
<div class="select-menu-filters">
|
||||
<div class="select-menu-text-filter">
|
||||
<input type="text" id="example-filter-field-2" class="form-control js-filterable-field js-navigation-enable" placeholder="Filter labels" aria-label="Type or choose a label" autocomplete="off">
|
||||
</div>
|
||||
<div class="select-menu-tabs">
|
||||
<ul>
|
||||
<li class="select-menu-tab">
|
||||
<a href="#url" data-tab-filter="branches" data-filter-placeholder="Filter for " class="js-select-menu-tab" aria-current="false">Branches</a>
|
||||
</li>
|
||||
<li class="select-menu-tab">
|
||||
<a href="#url" data-tab-filter="tags" data-filter-placeholder="Find a tag…" class="js-select-menu-tab selected" aria-current="true">Tags</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket selected" data-filterable-for="example-filter-field" data-filterable-type="substring" data-tab-filter="branches" role="menu">
|
||||
<input hidden="checkbox" name="example" value="">
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Branch 1
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Branch 2
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Branch 3
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Branch 4
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Branch 5
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket" data-filterable-for="example-filter-field" data-filterable-type="substring" data-tab-filter="tags" role="menu">
|
||||
<input hidden="checkbox" name="example" value="">
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Tag 1
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Tag 2
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Tag 3
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Tag 4
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text js-select-button-text">
|
||||
Tag 5
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Blankslate
|
||||
|
||||
Sometimes a select menu needs to communicate a "blank slate" where there's no content in the menu's list. Usually these include a clear call to action to add said content to the list. Swap out the contents of a `.select-menu-list` with the `.select-menu-blankslate` and customize it's contents as needed.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
Choose an item
|
||||
</button>
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-header js-navigation-enable" tabindex="-1">
|
||||
<button class="btn-link close-button js-menu-close" type="button"><%= octicon("x", :"aria-label" => "Close menu") %></button>
|
||||
<span class="select-menu-title">Options</span>
|
||||
</div>
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<div class="select-menu-blankslate">
|
||||
<%= octicon("check", :height => 32) %>
|
||||
<h3>Select menu blankslate</h3>
|
||||
<p>Some text here to describe why you're seeing a blankslate and how to go about fixing that up.</p>
|
||||
<button type="button" class="btn btn-sm btn-primary mt-3 mb-3">Deal with it</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Menu list items
|
||||
|
||||
Select menu list items have a few options available to them for more complex information patterns.
|
||||
|
||||
### Multi-line text
|
||||
|
||||
Sometimes the contents of your select menu list require a heading and a description instead of just a string. Select menus come with some default styles for such situations with the addition of a few classes and wrapping `<span>`s.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
<i>Multi line:</i>
|
||||
<span class="js-select-button">Choose an item</span>
|
||||
</button>
|
||||
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal subscription-menu-modal js-menu-content" aria-hidden="false">
|
||||
<div class="select-menu-header js-navigation-enable" tabindex="-1">
|
||||
<button class="btn-link close-button js-menu-close" type="button"><%= octicon("x", :"aria-label" => "Close menu") %></button>
|
||||
<span class="select-menu-title">Notifications</span>
|
||||
</div>
|
||||
<div class="select-menu-list js-navigation-container js-active-navigation-container" role="menu">
|
||||
<a href="#url" class="select-menu-item js-navigation-item selected" role="menuitem">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text">
|
||||
<span class="select-menu-item-heading">Not watching</span>
|
||||
<span class="description">Be notified when participating or @mentioned.</span>
|
||||
<span class="js-select-button-text hidden-select-button-text">
|
||||
Watch
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item" role="menuitem">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text">
|
||||
<span class="select-menu-item-heading">Watching</span>
|
||||
<span class="description">Be notified of all conversations.</span>
|
||||
<span class="js-select-button-text hidden-select-button-text">
|
||||
Stop watching
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item" role="menuitem">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<div class="select-menu-item-text">
|
||||
<span class="select-menu-item-heading">Ignoring</span>
|
||||
<span class="description">Never be notified.</span>
|
||||
<span class="js-select-button-text hidden-select-button-text">
|
||||
Stop ignoring
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### With avatars
|
||||
|
||||
Add avatars to a select menu to help indicate when a menu list item represents a user.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
Choose an item
|
||||
</button>
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-header js-navigation-enable" tabindex="-1">
|
||||
<button class="btn-link close-button js-menu-close" type="button"><%= octicon("x", :"aria-label" => "Close menu") %></button>
|
||||
<span class="select-menu-title">Options</span>
|
||||
</div>
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<a href="#url" class="select-menu-item selected js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-gravatar">
|
||||
<%= avatar_for(current_user, 20, :alt => "") %>
|
||||
</span>
|
||||
<span class="select-menu-item-text js-select-button-text">
|
||||
probot
|
||||
</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-gravatar">
|
||||
<%= avatar_for(current_user, 20, :alt => "") %>
|
||||
</span>
|
||||
<span class="select-menu-item-text js-select-button-text">
|
||||
probot
|
||||
</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-gravatar">
|
||||
<%= avatar_for(current_user, 20, :alt => "") %>
|
||||
</span>
|
||||
<span class="select-menu-item-text js-select-button-text">
|
||||
probot
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### With dismiss icon
|
||||
|
||||
Indicate how to toggle the selected state on a select menu list item with the addition of a dismiss icon.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
Choose an item
|
||||
</button>
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-header js-navigation-enable" tabindex="-1">
|
||||
<button class="btn-link close-button js-menu-close" type="button"><%= octicon("x", :"aria-label" => "Close menu") %></button>
|
||||
<span class="select-menu-title">Options</span>
|
||||
</div>
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<button type="button" class="select-menu-item selected js-navigation-item width-full">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">
|
||||
<%= octicon("x", :"aria-label" => "Click to remove") %>
|
||||
Item 1
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" class="select-menu-item js-navigation-item width-full">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">
|
||||
<%= octicon("x", :"aria-label" => "Click to remove") %>
|
||||
Item 2
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" class="select-menu-item js-navigation-item width-full">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">
|
||||
<%= octicon("x", :"aria-label" => "Click to remove") %>
|
||||
Item 3
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Menu alignment
|
||||
|
||||
By default select menus are automatically aligned to the top left corner of an element, but you can also customize that with a few utilities or custom display.
|
||||
|
||||
### Right aligned menus
|
||||
|
||||
When select menus are right aligned, you can also right-align the select menu's popover with `.select-menu-modal-right`.
|
||||
|
||||
```erb
|
||||
<div class="select-menu float-right select-menu-modal-right js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
Choose an item
|
||||
</button>
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-header js-navigation-enable" tabindex="-1">
|
||||
<button class="btn-link close-button js-menu-close" type="button"><%= octicon("x", :"aria-label" => "Close menu") %></button>
|
||||
<span class="select-menu-title">Options</span>
|
||||
</div>
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<a href="#url" class="select-menu-item selected js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 1</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 2</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 3</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Button options
|
||||
|
||||
Customize the select menu's trigger button by changing the button modifier class, enabling stateful text, and more.
|
||||
|
||||
### Style options
|
||||
|
||||
Since select menus are powered by JavaScript behaviors, the specific display of your select menu button is up to you and your use case.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
Default button
|
||||
</button>
|
||||
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<a href="#url" class="select-menu-item selected js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 1</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 2</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 3</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn btn-primary select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
Primary button
|
||||
</button>
|
||||
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<a href="#url" class="select-menu-item selected js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 1</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 2</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 3</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn btn-outline select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
Outline button
|
||||
</button>
|
||||
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<a href="#url" class="select-menu-item selected js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 1</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 2</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 3</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn-link select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
Link button
|
||||
</button>
|
||||
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<a href="#url" class="select-menu-item selected js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 1</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 2</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">Item 3</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Stateful text
|
||||
|
||||
Select menu buttons have the option of showing the current selection in the button itself. Wrap the string you intend to update with a `.js-select-button` and the select menu JavaScript will automatically update the contents of that element with the selected item's text.
|
||||
|
||||
Open the select menu below and click different options to see it in action.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="js-select-button">master</span>
|
||||
</button>
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-header js-navigation-enable" tabindex="-1">
|
||||
<button class="btn-link close-button js-menu-close" type="button"><%= octicon("x", :"aria-label" => "Close menu") %></button>
|
||||
<span class="select-menu-title">Options</span>
|
||||
</div>
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<a href="#url" class="select-menu-item selected js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">master</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">feature-branch</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">refactor-component-name</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Emphasized text
|
||||
|
||||
Sometimes you want to spice up your select menu with an emphasized label for the type of content within the menu. For example, you show `Branch:` in front of the current branch on our repository Code page. Within the button, wrap your string in an `<i>` element and you're good to go.
|
||||
|
||||
As shown below, emphasized text works great with the stateful text functionality mentioned above.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
<i>Branch:</i>
|
||||
<span class="js-select-button">master</span>
|
||||
</button>
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-header js-navigation-enable" tabindex="-1">
|
||||
<button class="btn-link close-button js-menu-close" type="button"><%= octicon("x", :"aria-label" => "Close menu") %></button>
|
||||
<span class="select-menu-title">Options</span>
|
||||
</div>
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<a href="#url" class="select-menu-item selected js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">master</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">feature-branch</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-text js-select-button-text">refactor-component-name</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Button avatars
|
||||
|
||||
Add an avatar to the button, like we do in our context switcher on the logged in dashboard.
|
||||
|
||||
```erb
|
||||
<div class="select-menu js-menu-container js-select-menu">
|
||||
<button class="btn select-menu-button js-menu-target" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
<div class="select-menu-button-gravatar js-select-button-gravatar">
|
||||
<%= avatar_for(current_user, 20, :"aria-label" => "") %>
|
||||
</div>
|
||||
<span class="js-select-button css-truncate css-truncate-target">probot</span>
|
||||
</button>
|
||||
<div class="select-menu-modal-holder">
|
||||
<div class="select-menu-modal js-menu-content">
|
||||
<div class="select-menu-header js-navigation-enable" tabindex="-1">
|
||||
<button class="btn-link close-button js-menu-close" type="button"><%= octicon("x", :"aria-label" => "Close menu") %></button>
|
||||
<span class="select-menu-title">Options</span>
|
||||
</div>
|
||||
<div class="select-menu-list js-navigation-container">
|
||||
<a href="#url" class="select-menu-item selected js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-gravatar">
|
||||
<%= avatar_for(current_user, 20, :"aria-label" => "") %>
|
||||
</span>
|
||||
<span class="select-menu-item-text js-select-button-text">
|
||||
probot
|
||||
</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-gravatar">
|
||||
<%= avatar_for(current_user, 20, :"aria-label" => "") %>
|
||||
</span>
|
||||
<span class="select-menu-item-text js-select-button-text">
|
||||
probot
|
||||
</span>
|
||||
</a>
|
||||
<a href="#url" class="select-menu-item js-navigation-item">
|
||||
<%= octicon("check", :class => "select-menu-item-icon") %>
|
||||
<span class="select-menu-item-gravatar">
|
||||
<%= avatar_for(current_user, 20, :"aria-label" => "") %>
|
||||
</span>
|
||||
<span class="select-menu-item-text js-select-button-text">
|
||||
probot
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
254
docs/pages/css/getting-started/contributing.md
Normal file
@ -0,0 +1,254 @@
|
||||
---
|
||||
title: Contributing
|
||||
---
|
||||
|
||||
Guidelines for contributing to GitHub's CSS.
|
||||
|
||||
- [Decision process for adding new styles](#decision-process-for-adding-new-styles)
|
||||
- [Step-by-step instructions for adding new styles](#step-by-step-instructions-for-adding-new-styles)
|
||||
- [Documentation structure](#documentation-structure)
|
||||
- [Primer modules](#primer-modules)
|
||||
- [Ship checklist](#ship-checklist)
|
||||
|
||||
## Decision process for adding new styles
|
||||
|
||||
### Components
|
||||
|
||||
[Components](/css/components) are frequently used visual patterns we've abstracted into a set of convenient styles, that would be otherwise difficult to achieve with utilities and layout objects.
|
||||
|
||||
Making a decision on whether new components should be added is done on a case by case basis. It's not always easy to make that decision but here are some questions you should ask yourself before moving forward with a pull request. The design systems team will help you make this decision.
|
||||
|
||||
* How often is this pattern used across the site?
|
||||
* Could these styles be achieved with existing components, objects, and utilities?
|
||||
* If your design is difficult to compose with current styles, does this highlight problems with existing components (such as overly-specific components, or missing objects and utilities)?
|
||||
* Is this a totally new pattern or should it be an extension of an existing component?
|
||||
* How is this pattern being implemented currently - have you identified problems with it’s current implementation that can be improved with adding a new pattern?
|
||||
* Is the desire for this new pattern a side-effect of lacking documentation or mis-understandings of use with current styles?
|
||||
* Are there special factors that need to be considered as to why the this pattern needs it’s own styles? Such legal issues, usability issues, or branding and trademarks?
|
||||
* Is this something that would be better handled by other front-end code rather than CSS?
|
||||
* Every new addition of CSS means we ask our users to download a larger CSS file, and we increase the maintenance work of our CSS framework. Does the convenience of adding these new styles outweigh those costs?
|
||||
|
||||
### Objects and utilities
|
||||
|
||||
Many of the same questions can be applied to objects and utilities, however the purpose of these styles is different:
|
||||
* [Objects](/css/objects) aren't concerned with thematic styles. They are for common display and positioning styles we find in page layouts and common content types.
|
||||
* [Utilities](/css/utilities) do one thing well and one thing only. These styles are immutable and therefore often use the `!important` tag. For this reason we aim not to change the properties of utilities very often. They often form the building blocks of our pages and when we introduce new ones we need to do so with care as we'll likely need to live with these styles for a long time. When assessing whether there is a need to add a new utility, consider these additional questions:
|
||||
- how does this new utility fit within our existing set of utilities? If it is an addition to an existing set then it should follow the same naming convention.
|
||||
- is it for a property that would likely need to be changed at different breakpoints? If so it may need responsive options.
|
||||
- if this style is part of a family of properties, do we need to consider adding the full set? Reasons for adding a full set could be that the other property values are often used, or that there would be a need to switch the property on and off (such as display or visibility).
|
||||
- does introducing this new utility cause any clashes or potential confusion with the use of existing utilities? If so, what steps can we take to avoid those issues.
|
||||
- does the utility have a connection with a set of variables? If so do the corresponding variables need to be updated?
|
||||
|
||||
## Step-by-step instructions for adding new styles
|
||||
|
||||
### Step 1: Open an issue
|
||||
It's usually better to open an issue before investing time in spiking out a new pattern. This gives the design systems team a heads up and allows other designers to share thoughts. Here's and outline of what's helpful to include in a new style issue:
|
||||
|
||||
1. What the pattern is and how it's being used across the site - post screenshots and urls where possible. If you need help identifying where the pattern is being used, call that out here and cc the relevant team and/or cc `@product-design` to help.
|
||||
2. Why you think a new pattern is needed (this should answer the relevant questions above).
|
||||
3. If you intend to work on these new styles yourself, let us know what your timeline and next steps are. If you need help and this is a dependency for shipping another project, please make that clear here and what the timeline is.
|
||||
4. Add the `type: new styles` label, or `type: refactor` where appropriate.
|
||||
|
||||
|
||||
### Step 2: Design and build the new styles
|
||||
|
||||
* You may want to explore the visual design for a new component before spiking it out in code. If so, continue in the issue and post your design mockups. Using the [Jekyll prototyping tool](https://github.com/github/design/blob/master/docs/resources/prototyping.md#jekyll) could also be a good option, it pulls in the production CSS so you can explore options in isolation before jumping into production code.
|
||||
* When you're ready, spike out a branch and build out your new component, object, or utilities according to the style guide principles, ensuring you follow our naming convention for each of the styles.
|
||||
* Refactor parts of the product where you think those new styles could be used to test they work for each use case. This does not mean for every instance, but enough of the key use cases to be sure the styles are flexible enough. To avoid blowing out your initial pull request we recommend you do larger amounts of refactoring in an additional branch.
|
||||
* When you're ready for review, add the `status: review needed` and the `design-systems` labels to your pull request. Follow the [ship checklist](#ship-checklist) for additional shipping steps.
|
||||
|
||||
### Step 3: Follow up with refactoring
|
||||
|
||||
New styles we add should be used in as many places as makes sense to in production. Often it takes time to refactor all instances to use the new styles in one pr, but you should ensure this is followed up on.
|
||||
* Add a tracking issue to the design systems repo with the label `type: refactor`. Add a task list with links to the code or pages that need updating. If you need help, request help in this issue.
|
||||
* Follow up with as many refactoring pull requests as you can make time for. This is an important part of the process and helps us reduce CSS bloat. Think of it as the project isn't finished until the new styles are being used everywhere they should be in production.
|
||||
|
||||
### Step 4: Feel awesome
|
||||
If you get to this step you've helped contribute to a style guide that many of your colleagues use on a daily basis, you've contributed to an open source project that's referenced and used by many others, and you've helped improve the usability and consistency of GitHub for our users. Thank you!
|
||||
|
||||
Let the [design systems team](https://github.com/github/design-systems) know if we can improve these guidelines and make following this process any easier.
|
||||
|
||||
|
||||
## Documentation structure
|
||||
|
||||
_**Note:** Documentation for Primer modules should live in the `README` of that module, see the [primer modules](#primer-modules) section below for more details. The [anatomy of a guide](#anatomy-of-a-guide) will work the same as part of a module README as well as regular markdown documentation._
|
||||
|
||||
The style guide takes a content first approach. Everything you see on the site is built from markdown files found in this folder.
|
||||
|
||||
Currently there’s a few levels of hierarchy. The top level is `styleguide/` inside the only content rendered is the `README.md` file on the style guide homepage.
|
||||
|
||||
The folders immediately in `styleguide/` represent top level navigation. There’s a little bit of setup needed to create a new top level navigation item.
|
||||
|
||||
* Create a new navigation tab in the navigation partial `github/github/app/views/navigation/_styleguide.html.erb`
|
||||
|
||||
Everything inside these top level navigation items gets built into the guide for that section. `README.md` files will be returned for sections (ie. `primer`, `js`, `ruby`, `branding`) when the user is on a section landing page ie `/primer/`.
|
||||
|
||||
### Anatomy of a guide
|
||||
|
||||
The anatomy of a style guide markdown file pretty straight forward, but has a few optional properties for making the page render special features.
|
||||
|
||||
Typically the file will look something like this:
|
||||
|
||||
---
|
||||
title: Avatar stack
|
||||
---
|
||||
|
||||
# Avatar stack
|
||||
|
||||
Stacked avatars can be used to show who is participating in thread when there is limited space available. When you hover over the stack, the avatars will reveal themselves. Optimally, you should put no more than 3 avatars in the stack.
|
||||
|
||||
```erb
|
||||
<span class="avatar-stack tooltipped tooltipped-s" aria-label="jonrohan, broccolini, and shawnbot">
|
||||
<%= avatar_for("jonrohan", 39, class: "avatar") %>
|
||||
<%= avatar_for("broccolini", 39, class: "avatar") %>
|
||||
<%= avatar_for("shawnbot", 39, class: "avatar") %>
|
||||
</span>
|
||||
```
|
||||
|
||||
Which consists of three parts:
|
||||
|
||||
1. **YML frontmatter** _(optional)_, similar to jekyll’s frontmatter, this is a way to pass info to the controller
|
||||
2. **Docs section** _(required)_, This is the section between the YML and the first `````html`
|
||||
3. **The example section** _(optional)_, This section is denoted by ````html` and will render into an example used in the page. This can contain rails helpers also eg. `<%= octicons 'fire' %>`
|
||||
|
||||
The options you have for the frontmatter are outlined below:
|
||||
|
||||
```yml
|
||||
---
|
||||
title: Document title
|
||||
---
|
||||
```
|
||||
|
||||
This is a nicer title for the guide section. If `title:` is not present, the helper will use the filename.
|
||||
|
||||
```yml
|
||||
---
|
||||
example_layout: toggle
|
||||
---
|
||||
```
|
||||
|
||||
Example layout will be applied to code blocks. By default it will just put the html into the page along with the syntax highlighted code, but for special cases, like animations, we need some customizations. The options for this property are **toggle, plain, none**. You can also [specify example layout on a per code block basis](#code-blocks).
|
||||
|
||||
```yml
|
||||
---
|
||||
example_template: icons
|
||||
---
|
||||
```
|
||||
|
||||
In cases where you don't want to use code blocks to render examples, you can choose to make your example templates. Just specify the template name here, and it will be rendered at the end of a guide.
|
||||
|
||||
```yml
|
||||
---
|
||||
status: Needs refactoring
|
||||
---
|
||||
```
|
||||
|
||||
The status option is a tag that will tag a module as a thing, displaying what state the feature is in.
|
||||
|
||||
|
||||
```yml
|
||||
---
|
||||
source: https://github.com/github/github/tree/master/app/assets/stylesheets/components/x.scss
|
||||
---
|
||||
```
|
||||
|
||||
The source option will let you point the document to the source file. **This is only necessary for components that still live in the `github/github` repo**; Primer CSS source files will have the `source` field populated automatically.
|
||||
|
||||
### Special tags
|
||||
|
||||
#### Tables of contents
|
||||
|
||||
In the style guide you can add a `{:toc}` tag to have a table of contents automatically generated.
|
||||
|
||||
#### Code blocks
|
||||
|
||||
When using code blocks for demo purposes, you can choose to render each of the blocks differently by specifying the layout in the info string. For example if you want to use `toggle` as the layout for a code block:
|
||||
|
||||
```markdown
|
||||
```html layout=toggle
|
||||
<div class="p-5">Animation</div>
|
||||
```
|
||||
```
|
||||
|
||||
## Primer modules
|
||||
|
||||
Modules are created for all the styles we include in the Primer framework. Modules are folders with a specific structure that include CSS, a `package.json`, and other files for publishing to repositories in our GitHub Primer organization and NPM.
|
||||
|
||||
The source of truth for our CSS will be in the github/github codebase, but we publish updates to NPM whenever styles in github/github are changed. By publishing to NPM we are able to distribute our reusable modules to other GitHub properties.
|
||||
|
||||
Modules are grouped into three packages:
|
||||
- **primer-core:** modules that are used for dotcom as well as marketing websites
|
||||
- **primer-product:** modules that are only used on dotcom
|
||||
- **primer-marketing:** modules that are only used on marketing websites and occasionally for promotional content on dotcom
|
||||
|
||||
You can add module packages by including any or all of the following imports in your bundle:
|
||||
```
|
||||
@import "primer-core/index.scss"
|
||||
@import "primer-product/index.scss"
|
||||
@import "primer-marketing/index.scss"
|
||||
```
|
||||
|
||||
### Creating a module
|
||||
|
||||
1. Create a new repository on https://github.com/primer that will be the location for your module. Only the design systems team have write access to that repository. If you don't have access, ask in #design-systems and someone will create a folder for you.
|
||||
2. Create a new folder inside one of the primer directories (core, product, or marketing), and within the appropriate styles folder (components, objects, utilities etc.). This folder cannot be inside another module with a `package.json` file.
|
||||
3. Inside the folder you'll need at least a `package.json` file. Here is an example of what you'll need in the package file. The main thing it's looking for are `name, version, and repository`. The publish script uses this to push to the distribution repository.
|
||||
|
||||
```js
|
||||
{
|
||||
"name": "module-name",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"homepage": "https://github.com/styleguide",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/primer/primer.git",
|
||||
"main": "utilities.scss",
|
||||
"bugs": {
|
||||
"url": "https://github.com/primer/primer/issues"
|
||||
},
|
||||
"author": "GitHub, Inc.",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"primer",
|
||||
"css",
|
||||
"github",
|
||||
"primercss"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
5. The directory layout should typically be like this:
|
||||
|
||||
```
|
||||
a-module/
|
||||
├── lib/
|
||||
│ ├── partial.scss
|
||||
│ └── partial.scss
|
||||
├── index.scss
|
||||
├── README.md
|
||||
└── package.json
|
||||
```
|
||||
|
||||
Create a stylesheet named `index.scss`. In this file include a list of relative imports for the partials required, like the example below:
|
||||
|
||||
```scss
|
||||
@import "primer-support/index.scss";
|
||||
@import "./lib/button.scss";
|
||||
@import "./lib/button-group.scss";
|
||||
```
|
||||
|
||||
Add a `README` to give some info on the module and how to install it, and a documentation section. Here's an example of the [buttons README](https://github.com/primer/primer/blob/master/README.md).
|
||||
|
||||
### Publishing changes and new modules
|
||||
|
||||
Once you have your directory setup, you will be ready to publish the module.
|
||||
|
||||
To publish, there are two requirements. First, you must be on the `master` branch. Second, all changes should be committed and synced with the remote `master`. These requirements protect us from changes being overwritten. Once you're on `master` run `script/css-modules --help` to get a list of all the available commands. This script will help with versioning and publishing.
|
||||
|
||||
#### Versioning
|
||||
|
||||
All the individual Primer modules are [semver](http://semver.org/) versioned. This helps others know when a change is a patch, minor, or breaking change.
|
||||
|
||||
To understand what choice to make, you'll need to understand semver and know if one of the changes shown is a major, minor, or patch. Semver is confusing at first, so I recommend reviewing [semver](http://semver.org/) and/or ask in [#design-systems](https://github.slack.com/archives/design-systems) or and experienced open-source contributor.
|
90
docs/pages/css/getting-started/index.md
Normal file
@ -0,0 +1,90 @@
|
||||
---
|
||||
title: Getting started
|
||||
---
|
||||
|
||||
Our CSS framework, Primer, is [open-sourced on GitHub](https://github.com/primer/primer) and [hosted on npm](https://www.npmjs.com/package/primer). Our modules are grouped into three packages: [primer-core](https://github.com/primer/primer/tree/master/modules/primer-core), [primer-product](https://github.com/primer/primer/tree/master/modules/primer-product), and [primer-marketing](https://github.com/primer/primer/tree/master/modules/primer-marketing). `primer-core` contains packages used in both product (github.com) and marketing (logged out homepage). To install all of primer, you can use [primer](https://github.com/primer/primer) which is a grouping of core, product and marketing.
|
||||
|
||||
## Installing via npm
|
||||
|
||||
We recommend using npm to install primer because of how easy npm is for managing dependencies.
|
||||
|
||||
### Before you start
|
||||
|
||||
Primer packages require npm version 3 or above. You can check what version you have by running `npm -v`. If you have a version that's older than 3.0, you can update it by running `npm install npm@latest -g`. For more info, read the [npm install docs](https://docs.npmjs.com/getting-started/installing-node).
|
||||
|
||||
### Initialize npm project
|
||||
|
||||
Begin by initializing your project with a `package.json` file. You can read more on how to do this [in the npm documentation](https://docs.npmjs.com/getting-started/using-a-package.json#creating-a-packagejson).
|
||||
|
||||
### Install primer modules
|
||||
|
||||
Install the primer modules you wish to use by running the npm install command. This will install the module and all the dependencies into the `node_modules` directory.
|
||||
|
||||
```
|
||||
npm install primer --save
|
||||
```
|
||||
|
||||
### For a Jekyll site
|
||||
|
||||
Make sure you have [Jekyll](https://jekyllrb.com/) version `3.3.1` or greater with:
|
||||
|
||||
```
|
||||
jekyll -v
|
||||
```
|
||||
|
||||
If you have an older version, follow the instructions in the [upgrading docs](https://jekyllrb.com/docs/upgrading/).
|
||||
|
||||
Once you have Jekyll up and running, you will need to add this configuration to your `_config.yml` file. This let's the sass compiler know where your code lives.
|
||||
|
||||
```yml
|
||||
sass:
|
||||
# Where you keep your scss files
|
||||
sass_dir: assets/css/
|
||||
# Where sass should look for other scss
|
||||
load_paths:
|
||||
- node_modules/
|
||||
```
|
||||
|
||||
It's best practice to import all of this scss into one file, usually named `index.scss`. From this file you'll import your primer code and any other custom code you write.
|
||||
|
||||
```scss
|
||||
@import "primer-core/index.scss";
|
||||
// These files live in the same directory as the index file.
|
||||
@import "./custom-1.scss";
|
||||
@import "./custom-2.scss";
|
||||
```
|
||||
|
||||
Here's an example of how it might look if you installed only a few primer components with some custom variable overrides. The `$blue` uses the default primer blue in the text utilities, then the new blue in `"custom-that-uses-primer-variables.scss"` and `.foo`.
|
||||
|
||||
```scss
|
||||
@import "primer-utilities/index.scss";
|
||||
@import "primer-buttons/index.scss";
|
||||
|
||||
// Import color variables for custom code
|
||||
@import "primer-support/index.scss";
|
||||
|
||||
// Override default blue
|
||||
$blue: #0000ff;
|
||||
|
||||
@import "./custom-that-uses-primer-variables.scss";
|
||||
|
||||
.foo {
|
||||
background: $blue;
|
||||
font-size: $h2-size;
|
||||
color: $text-gray;
|
||||
}
|
||||
```
|
||||
|
||||
Don't forget to add the compiled CSS to the `<head>` section of your page.
|
||||
|
||||
```html dead
|
||||
<link href="path/to/style.css" rel="stylesheet">
|
||||
```
|
||||
|
||||
## Using primer on a static site
|
||||
|
||||
You won't need to install any node modules for a static site, you can use the built CSS. The best thing to do is to [download the built CSS](https://unpkg.com/primer/build/build.css) from the npm module and host it yourself. If that's not an option, you can include a CDN link in your html:
|
||||
|
||||
```html dead
|
||||
<link href="https://unpkg.com/primer/build/build.css" rel="stylesheet">
|
||||
```
|
72
docs/pages/css/index.md
Normal file
@ -0,0 +1,72 @@
|
||||
import {Box, Flex, Heading, Link, Text} from '@primer/components'
|
||||
import {
|
||||
HeaderImage,
|
||||
MetaPackageBox,
|
||||
PrimerPackageBox,
|
||||
PrimitivesOverview,
|
||||
StylesOverview
|
||||
} from '../../src/landing'
|
||||
import {CONTENT_MAX_WIDTH} from '../../src/constants'
|
||||
import {name, version} from 'primer/package.json'
|
||||
import packages from './packages.json'
|
||||
|
||||
export const Hero = () => (
|
||||
<Box bg="black">
|
||||
<Box maxWidth={CONTENT_MAX_WIDTH} p={6} mx="auto" mb={3}>
|
||||
<Box mt={4} mb={4}>
|
||||
<Heading color="blue.4" fontSize={7} pb={3} m={0}>
|
||||
Primer CSS
|
||||
</Heading>
|
||||
<Text as="div" color="blue.2" fontSize={2} mb={4}>
|
||||
v{version}
|
||||
</Text>
|
||||
<Box as={HeaderImage} mb={6} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
|
||||
# Introduction
|
||||
|
||||
Our goal is to create a system that enables us to build consistent user experiences with ease, yet with enough flexibility to support the broad spectrum of GitHub websites. This goal is embedded in our design and code decisions. Our approach to CSS is influenced by Object Oriented CSS principles, functional CSS, and BEM architecture.
|
||||
|
||||
## Highly reusable, flexible styles
|
||||
|
||||
Styles can be mixed and matched to achieve many different layouts, independent of their location. These styles fall into three categories:
|
||||
|
||||
<StylesOverview m={6} />
|
||||
|
||||
## Systematically designed for GitHub
|
||||
|
||||
Primer is built upon systems that form the foundation of our styles such as spacing, typography, and color. This systematic approach helps ensure our styles are consistent and interoperable with each other.
|
||||
|
||||
<PrimitivesOverview />
|
||||
|
||||
## Primer packages
|
||||
|
||||
Each component or group of styles is packaged up and distributed via npm. Primer includes 23 packages that are grouped into useful meta-packages for easy install. Each package and meta-package is independently versioned and distributed via npm, so it's easy to include all or part of Primer within your own project.
|
||||
|
||||
<PrimerPackageBox data={packages.primer} count={Object.keys(packages).length - 1} mb={4} />
|
||||
|
||||
<Flex justifyContent="space-around" mb={6}>
|
||||
<MetaPackageBox title="Core" data={packages['primer-core']} width={1/3}>
|
||||
The core package contains modules that are shared between GitHub product and marketing websites.
|
||||
</MetaPackageBox>
|
||||
<MetaPackageBox title="Product" data={packages['primer-product']} width={1/3}>
|
||||
The product package contains modules that are used on GitHub product websites.
|
||||
</MetaPackageBox>
|
||||
<MetaPackageBox title="Marketing" data={packages['primer-marketing']} width={1/3}>
|
||||
The marketing package contains modules that are used on GitHub marketing websites.
|
||||
</MetaPackageBox>
|
||||
</Flex>
|
||||
|
||||
|
||||
<div class="bg-gray py-6">
|
||||
<div class="d-flex flex-wrap flex-md-nowrap px-6 gutter-lg">
|
||||
<div class="col-12 col-md-9 pr-0 pr-lg-2">
|
||||
<h3 class="f3 text-normal m-0">Use Primer in your project</h3>
|
||||
<p class="my-3">Pick and choose what you need. Install the entire Primer bundle or individual packages via npm.</p>
|
||||
<a href="/css/getting-started" class="btn btn-outline">Installation instructions</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
5
docs/pages/css/objects/index.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Objects
|
||||
---
|
||||
|
||||
Objects help us with common layout patterns but aren't concerned with thematic styles. Examples include the table object, the grid, and the media object. Objects essentially provide some scaffolding for layouts and should be able to be used with other objects, components, and utilities.
|
196
docs/pages/css/principles/accessibility.md
Normal file
@ -0,0 +1,196 @@
|
||||
---
|
||||
title: Accessibility
|
||||
---
|
||||
|
||||
Accessibility is everyone’s responsibility, and the purpose of this document is to provide general accessibility guidelines to developers and designers.
|
||||
|
||||
{:toc}
|
||||
|
||||
## Overview
|
||||
|
||||
Our products should be accessible to all. At minimum, features should comply to the requirements listed in [508 Reference Guide - 1194.22](https://www.access-board.gov/guidelines-and-standards/communications-and-it/about-the-section-508-standards/guide-to-the-section-508-standards/web-based-intranet-and-internet-information-and-applications-1194-22) from the US Access Board, and conform to [Web Content Accessibility Guidelines 2.0](https://www.w3.org/TR/WCAG20/#conformance) at Level AA.
|
||||
|
||||
Making our products accessible benefits everyone, not just people with disabilities. Below are some examples of use cases in which accessibility is important:
|
||||
|
||||
- **Visual**: blindness, low vision, color blindness, using a screen reader or related assistive tech for lifestyle reasons (e.g. long car commute), machine readability and screen scraping technologies
|
||||
|
||||
- **Hearing**: deafness, hearing impairment, speech impairment, using closed captioning or other assistive features for lifestyle reasons (e.g. coworking in a loud coffee shop)
|
||||
|
||||
- **Cognitive**: including short-term memory issues, dyslexia, learning disabilities, trying to work or consume content while distracted or multitasking, etc.
|
||||
|
||||
- **Mobility**: mobility impairments, repetitive stress injuries, power users who love keyboard shortcuts, busy parents holding a sleeping child while trying to operate a computer with one hand, etc.
|
||||
|
||||
## General guidelines
|
||||
|
||||
### Semantic markup
|
||||
|
||||
Always use semantic HTML elements, like `button`, `ul`, `nav`. Most modern browsers implement the accessibility features outlined in the specs for these elements; without them, elements will need additional [ARIA attributes and roles](https://www.w3.org/WAI/PF/aria) to be recognized by assistive technologies.
|
||||
|
||||
Elements like `h1`-`h6`, `nav`, `footer`, `header` have [meaningful roles](https://www.w3.org/WAI/PF/aria/roles) assigned, so use them, and use them carefully. This can help assistive technologies read the page better and help users find information quicker.
|
||||
|
||||
Only use a `div` or a `span` to markup up content when there isn't another HTML element that would semantically be more appropriate, or when an element is needed exclusively for applying CSS styles or JS behaviors.
|
||||
|
||||
```html
|
||||
<!-- Do: Button can be focused and tabbed to. -->
|
||||
<button type="button" class="btn">Send</button>
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Don't: Button can't be focused and tabbed to. -->
|
||||
<span class="btn">Send</span>
|
||||
```
|
||||
|
||||
> More on semantic markup:
|
||||
>
|
||||
> - [Semantic Structure – WebAIM](http://webaim.org/techniques/semanticstructure/)
|
||||
|
||||
### Keyboard accessibility
|
||||
|
||||
Make sure elements can be reached via tabbing, and actions can be triggered with a keyboard. Using semantic markup is especially important in this case as it ensures that actionable elements are tabbable and triggerable without a mouse. Note that elements positioned off-screen are still tabbable, but those hidden with `display: none` or `visibility: hidden` are effectively removed from content since they are neither read by screen readers nor reachable by keyboard.
|
||||
|
||||
Tab order is determined by the order of elements in the DOM, and not by their visual positioning on the page after CSS is applied. CSS properties `float` and `order` for flex items are two common sources for disconnection between visual and DOM order.
|
||||
|
||||
The `tabindex` attribute should only be used when absolutely necessary.
|
||||
|
||||
- `tabindex=0/-1` makes an element focusable, while `tabindex=0` also includes the element in the normal tab order. In both cases, keyboard triggers of the element still require scripting, so where possible, use [interactive content](http://w3c.github.io/html/dom.html#kinds-of-content-interactive-content) instead.
|
||||
|
||||
- `tabindex=1+` takes an element to the very front of the default tab order. When it's applied, the element's visual positioning is no longer indicative of its tab order, and the only way to find out where an element is would be by tabbing through the page. Therefore, unless a page is carefully designed around elements with positive tabindex, it is very error-proned, and thus currently prohibited in github.com.
|
||||
|
||||
Finally, bear in mind that some assistive technologies have keyboard combinations of their own that will override the combination on the web page, so don't rely on keyboard shortcuts as the only alternative to mouse actions.
|
||||
|
||||
```html
|
||||
<!-- Do: Tab order can be guessed. -->
|
||||
<button type="button" class="btn">1</button>
|
||||
<button type="button" class="btn">2</button>
|
||||
<button type="button" class="btn">3</button>
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Don't: The second button's tab order is unexpected. -->
|
||||
<button type="button" class="btn">1</button>
|
||||
<button type="button" class="btn" tabindex="1">2</button>
|
||||
<button type="button" class="btn">3</button>
|
||||
```
|
||||
|
||||
> More on keyboard accessibility:
|
||||
>
|
||||
> - [Focus Order – Understanding WCAG 2.0](https://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-focus-order.html)
|
||||
> - [Time to revisit accesskey? by Léonie Watson](http://tink.uk/time-to-revisit-accesskey/)
|
||||
> - [Flexbox & the keyboard navigation disconnect by Léonie Watson](http://tink.uk/flexbox-the-keyboard-navigation-disconnect/)
|
||||
|
||||
### Visual accessibility
|
||||
|
||||
Be mindful when using small font size, thin font weight, low contrast colors in designs as it can severely affect usability.
|
||||
|
||||
Instead of relying solely on color to communicate information, always combine color with another factor, like shape or position change. This is important because some colors can be hard to tell apart due to color blindness or weak eyesight.
|
||||
|
||||
> More on visual accessibility:
|
||||
>
|
||||
> - [Use of Color – Understanding WCAG 2.0](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-without-color.html)
|
||||
> - [Contrast – Understanding WCAG 2.0](http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html)
|
||||
|
||||
### Text and labels
|
||||
|
||||
Make sure text-based alternative is always available when using icons, images, and graphs, and that the text by itself presents meaningful information.
|
||||
|
||||
```html
|
||||
<!-- Do: Link text communicates destination. -->
|
||||
<p>Find out more about <a href="#url">GitHub Enterprise pricing</a>.<br></p>
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Don't: "Click here" is not meaningful. -->
|
||||
<p>To find out more about GitHub Enterprise pricing, <a href="#url">click here</a>.</p>
|
||||
```
|
||||
|
||||
Use `title` to add information on top of existing content.
|
||||
|
||||
```html
|
||||
<a title="@octocat's repositories" href="https://github.com/octocat?tab=repositories">octocat</a>
|
||||
```
|
||||
|
||||
Use `aria-label` when there is no text.
|
||||
|
||||
```html
|
||||
<a href="https://help.github.com/"><%= octicon("question", :"aria-label" => "Help") %></a>
|
||||
```
|
||||
|
||||
### Link responsibly
|
||||
|
||||
Navigating from a list of all the links on a given web page is very common for assistive technology users. We should make sure that the link text itself is meaningful and unique, and there should be as few duplicated references as possible.
|
||||
|
||||
> More on link responsibly:
|
||||
>
|
||||
> - [Link Purpose – Understanding WCAG 2.0](https://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-refs.html)
|
||||
|
||||
### Dynamic content
|
||||
|
||||
When using JavaScript to change the content on the page, always make sure that screen reader users are informed about the change. This can be done with `aria-live`, or focus management.
|
||||
|
||||
> More on dynamic content:
|
||||
>
|
||||
> - [ARIA Live Regions – MDN](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions)
|
||||
|
||||
### Focus management
|
||||
|
||||
Focus management is useful for informing users about updated content, and for making sure users land on the next useful section after performing an action. This means using scripts to change user’s currently focused element, and when focus changes, screen readers will read out change.
|
||||
|
||||
### Accessible forms
|
||||
|
||||
It is common for assistive technology users to jump straight to a form when using a website, so make sure most relevant information is in the form and is labelled properly. Labels and inputs should be associated with the `label[for]` and `input[id]`, and help texts should either be part of the label or be associated with `aria-describedby`.
|
||||
|
||||
```html
|
||||
<!-- Do: Click "Email" label to focus on the input, and help text is read out by screen readers when focusing on the input. -->
|
||||
<label for="test_input">Email</label><br>
|
||||
<input id="test_input" aria-describedby="test_input_help_good" class="width-full my-1" type="email">
|
||||
<div id="test_input_help_good" class="f6">Please enter an email ending with @github.com.</div>
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Don't: Input and label are not associated, and help text is not read out by screen reader when focusing on the input. -->
|
||||
<label>Email</label><br>
|
||||
<input type="email" class="width-full my-1">
|
||||
<div id="test_input_help_bad" class="f6">Please enter an email ending with @github.com.</div>
|
||||
```
|
||||
|
||||
## Development tools
|
||||
|
||||
### Linting
|
||||
|
||||
We currently run basic linting [against positive `tabindex`](https://github.com/github/github/blob/8546895623677abc70f61951642f32c16de231a1/test/fast/linting/accessible_tabindex_test.rb) and [anchor links with `href="#"`](https://github.com/github/github/blob/8546895623677abc70f61951642f32c16de231a1/test/rubocop/cop/rails/link_href.rb).
|
||||
|
||||
We do client side scanning of inaccessible elements in development environment. The inaccessible elements will be styled with red borders with an onclick alert and console warnings.
|
||||
|
||||
### External tools
|
||||
|
||||
- Google Chrome provides an [Accessibility Developer Tools extension](https://chrome.google.com/webstore/detail/accessibility-developer-t/fpkknkljclfencbdbgkenhalefipecmb). Once installed, go to Audits tab in developer tools, tick Accessibility. It scans the page for violations and suggests solutions.
|
||||
|
||||
- If you're working on a design that uses color to communicate something, grab the [Chromatic Vision Simulator app](https://itunes.apple.com/tw/app/chromatic-vision-simulator/id389310222?mt=8), this will let you use your iPhone camera to see what a colorblind person would see.
|
||||
|
||||
- Check out [the Web Accessibility showcase on GitHub](https://github.com/showcases/web-accessibility). GitHubbers are free to add more projects to the showcase.
|
||||
|
||||
## Manual testing
|
||||
|
||||
### Screen reader
|
||||
|
||||
To use VoiceOver, the built-in screen reader for Mac, just hit <kbd>⌘</kbd> + <kbd>F5</kbd>. This will start voice narration and display most of the spoken information in a text box.
|
||||
|
||||
Use <kbd>alt</kbd> + <kbd>control</kbd> + <kbd>left/right</kbd> to navigate a web page. <kbd>alt</kbd> + <kbd>control</kbd> + <kbd>space</kbd> to click on an element. For more help with VoiceOver, check out the built-in tutorial in `System Preferences > Accessibility > VoiceOver > Open VoiceOver Training`.
|
||||
|
||||
Keep in mind that behaviors in different screen readers can differ when navigating the same web page; just like designing for different browsers, when it comes to accessibility, it is not just the implementation of the browsers that can be different, but also the ones of assistive softwares.
|
||||
|
||||
## Internal resources
|
||||
|
||||
1. You can mention these teams when looking for help:
|
||||
|
||||
- [@github/accessibility](https://github.com/orgs/github/teams/accessibility): GitHubbers interested in accessibility related topics and work on website accessibility issues.
|
||||
- [@github/colorblind](https://github.com/orgs/github/teams/colorblind): GitHubbers who are interested in accessibility for colorblindness.
|
||||
|
||||
2. Go to #accessibility Slack channel to ask questions or discuss accessibility issues.
|
||||
3. Check [github/accessibility repository](https://github.com/github/accessibility) for information on events or learning resources.
|
||||
|
||||
## User support
|
||||
|
||||
Accessibility is a priority for us, if you ever encounter accessibility related issues when using github.com, please don’t hesitate to reach out to us via [the contact page](https://github.com/contact) or email us at [support@github.com](mailto:support@github.com), we will try our best to assist.
|
||||
|
||||
For information about the accessibility compliance of GitHub products, please refer to our [VPAT report, outlining §508 accessibility information for GitHub.com, GitHub Enterprise, and GitHub Desktop](https://government.github.com/accessibility/).
|
86
docs/pages/css/principles/html.md
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
title: HTML
|
||||
---
|
||||
|
||||
{:toc}
|
||||
|
||||
## General formatting
|
||||
|
||||
* Use soft-tabs with a two space indent. Spaces are the only way to guarantee code renders the same in any person's environment.
|
||||
* Paragraphs of text should always be placed in a `<p>` tag. Never use multiple `<br>` tags.
|
||||
* Items in list form should always be in `<ul>`, `<ol>`, or `<dl>`. Never use a set of `<div>` or `<p>`.
|
||||
* Every form input that has text attached should utilize a `<label>` tag. **Especially radio or checkbox elements.**
|
||||
* Even though quotes around attributes is optional, always put quotes around attributes for readability.
|
||||
* Avoid writing closing tag comments, like `<!-- /.element -->`. This just adds to page load time. Plus, most editors have indentation guides and open-close tag highlighting.
|
||||
* Avoid trailing slashes in self-closing elements. For example, `<br>`, `<hr>`, `<img>`, and `<input>`.
|
||||
* Don't set `tabindex` manually—rely on the browser to set the order.
|
||||
|
||||
```html dead={true}
|
||||
<p class="line-note m-0" data-attribute="106">
|
||||
This is my paragraph of special text.
|
||||
</p>
|
||||
```
|
||||
|
||||
## Boolean attributes
|
||||
|
||||
Many attributes don't require a value to be set, like `disabled` or `checked`, so don't set them.
|
||||
|
||||
```html dead={true}
|
||||
<input type="text" disabled>
|
||||
|
||||
<input type="checkbox" value="1" checked>
|
||||
|
||||
<select>
|
||||
<option value="1" selected>1</option>
|
||||
</select>
|
||||
```
|
||||
|
||||
For more information, [read the WhatWG section](http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#boolean-attributes).
|
||||
|
||||
## Lean markup
|
||||
|
||||
Whenever possible, avoid superfluous parent elements when writing HTML. Many times this requires iteration and refactoring, but produces less HTML. For example:
|
||||
|
||||
```html dead={true}
|
||||
<!-- Not so great -->
|
||||
<span class="avatar">
|
||||
<img src="https://github.com/github.png">
|
||||
</span>
|
||||
|
||||
<!-- Better -->
|
||||
<img class="avatar" src="https://github.com/github.png">
|
||||
```
|
||||
|
||||
## Forms
|
||||
|
||||
* Lean towards radio or checkbox lists instead of select menus.
|
||||
* Wrap radio and checkbox inputs and their text in `<label>`s. No need for `for` attributes here—the wrapping automatically associates the two.
|
||||
* Form buttons should always include an explicit `type`. Use primary buttons for the `type="submit"` button and regular buttons for `type="button"`.
|
||||
* The primary form button must come first in the DOM, especially for forms with multiple submit buttons. The visual order should be preserved with `float: right;` on each button.
|
||||
|
||||
## Tables
|
||||
|
||||
Make use of `<thead>`, `<tfoot>`, `<tbody>`, and `<th>` tags (and `scope` attribute) when appropriate. (Note: `<tfoot>` goes above `<tbody>` for speed reasons. You want the browser to load the footer before a table full of data.)
|
||||
|
||||
```html dead={true}
|
||||
<table summary="This is a chart of invoices for 2011.">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Table header 1</th>
|
||||
<th scope="col">Table header 2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>Table footer 1</td>
|
||||
<td>Table footer 2</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Table data 1</td>
|
||||
<td>Table data 2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
```
|
222
docs/pages/css/principles/index.md
Normal file
@ -0,0 +1,222 @@
|
||||
---
|
||||
title: Principles
|
||||
---
|
||||
|
||||
The CSS styleguide enables us to create a consistent user experience across GitHub, manage CSS bloat, and make our CSS easier to maintain. These principles should serve as guidelines for how we write and use CSS.
|
||||
|
||||
{:toc}
|
||||
|
||||
|
||||
## Styleguide driven design and development
|
||||
|
||||
Every time new CSS is added it increases our CSS bloat, CSS maintenance, and can add to inconsistencies in the user experience of our site. If we follow a practice of designing with styles in the styleguide first and try to implement our designs with only styles in the style guide first, we reduce the risk of deviating away from these styles.
|
||||
|
||||
_If new styles are needed:_
|
||||
* Use global variables where appropriate, such as spacing, typography, and color variables.
|
||||
* Write styles in a way that can be folded back into the global style guide should it become a common pattern, i.e. following our principles for naming conventions, components, objects, and utilities as listed below.
|
||||
|
||||
|
||||
## Obvious and transparent
|
||||
|
||||
Many designers and developers will edit and add to our CSS. Write CSS in a way that make it obvious and transparent what the CSS does, and support with comments wherever needed.
|
||||
|
||||
**Class names:**
|
||||
|
||||
* Use class names that make it easy to understand what styles are being applied.
|
||||
* Keep naming conventions consistent so that it's easier to internalize and understand class names. For components follow the [BEM-style syntax](#bem-style-naming-and-structure) described below.
|
||||
* Don't use class names that suggest the styles do one thing, but instead have hidden properties.
|
||||
* Use presentational or functional class names and avoid tying class names for global styles to as specific page or feature.
|
||||
|
||||
```scss
|
||||
// Do
|
||||
.bg-white { background-color: #fff; }
|
||||
|
||||
.box {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
// Don't
|
||||
.bg-white {
|
||||
color: #111;
|
||||
background-color: #fff;
|
||||
border: #ccc;
|
||||
}
|
||||
|
||||
.teams-side-panel {
|
||||
width: 330px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
**Sass:**
|
||||
|
||||
* Choose verbose over clever. A little duplication is worthwhile if it adds clarity.
|
||||
* Don't prioritize being DRY if it means it's hard to read and understand, creates dependencies, or hides what the code is really doing.
|
||||
* Avoid overusing pre-processor features that make the code less approachable. Keep it CSS'y and limit the use of Sass features like nesting, variables, functions. For more detail on this check out our [Sass guidelines and lint rules](/css/principles/scss).
|
||||
|
||||
|
||||
## Components
|
||||
|
||||
Components make it easier to mark up a set of elements that are commonly grouped together with similar visual styles. Their base styles are shared and variants are added with modifier classes, so usually components are comprised of multiple classes. Most importantly, components should be highly reusable across the site, rather than built for specific pages or features. To achieve this:
|
||||
|
||||
* **Separate structure and skin:** This means to define repeating visual features (like background and border styles) as separate “skins” that you can mix-and-match with your various components to achieve a large amount of visual variety without much code.
|
||||
* **Separate container and content:** Essentially, this means “rarely use location-dependent styles”. A component should look the same no matter where you put it.
|
||||
|
||||
```scss
|
||||
// structure
|
||||
.flash {...}
|
||||
|
||||
// skin
|
||||
.flash-error {...}
|
||||
```
|
||||
|
||||
## Objects
|
||||
|
||||
Objects help us with common layout patterns but aren't concerned with thematic styles. Examples include the table object, the grid, and the media object. Objects essentially provide some scaffolding for layouts and should be able to be used with other objects, components, and utilities.
|
||||
|
||||
Example:
|
||||
|
||||
```html
|
||||
<div class="TableObject">
|
||||
<div class="TableObject-item TableObject-item--primary">
|
||||
<input class="input-block" type="text" placeholder="Long flexible input form">
|
||||
</div>
|
||||
<div class="TableObject-item">
|
||||
<button class="btn ml-3" type="button">Button</button>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## BEM-style naming and structure
|
||||
|
||||
Components and objects should follow the [Block Element Modifier](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/) (BEM) model in terms of structure. We've chosen to use a modified form of BEM syntax using [PascalCase](https://en.wikipedia.org/wiki/PascalCase) for the block name, hyphens and lowercase or elements, and double-hyphens and lowercase for modifiers. When a Block class requires two words use PascalCase, for example `ProgressBar`.
|
||||
|
||||
* **Block**: A block includes all of the base styles you want shared across every variation of a component. Minimal thematic styling should be applied to blocks, particularly when variations of a component include visual variations. Apply additional styles with modifers rather than overriding base styles.
|
||||
* **Element**: An element is part of a component. Elements should work together with other elements and can have modifiers. Element styles should not override block styles - this often results in applying more styles directly to elements rather than having styles flow down from the parent level.
|
||||
* **Modifier**: A modifier is a variation that can be applied to the base component or to an element within the component. Modifiers shouldn't override block styles, they should add onto them.
|
||||
|
||||
```scss
|
||||
// block
|
||||
.Box {...}
|
||||
|
||||
// elements
|
||||
.Box-header {...}
|
||||
.Box-body {...}
|
||||
.Box-footer {...}
|
||||
|
||||
// modifiers
|
||||
.Box--blue {...}
|
||||
.Box--red {...}
|
||||
.Box-header--large {...}
|
||||
```
|
||||
|
||||
## Property order
|
||||
For base styles, components, and objects, follow the following property order.
|
||||
|
||||
```scss
|
||||
.element {
|
||||
// 1. Positioning
|
||||
// 2. Box model (display, float, width, etc)
|
||||
// 3. Typography (font, line-height, text-*)
|
||||
// 4. Visuals (background, border, opacity)
|
||||
// 5. Misc (CSS3 properties)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Utilities
|
||||
Utilities provide the building blocks for layout and handle a range common use cases that help us avoid writing custom styles. When we need to add some margin or padding, rather than adding a new selector specific to that use case, we can use utilities. This helps us reduce the number of unique property-value pairs, and helps us keep more consistent styles across the site.
|
||||
|
||||
* Utilities should do one job well and one job only, are immutable, and on occasion are an acceptable approach to overriding component styles.
|
||||
* Utility class-names should be transparent and clearly describe the job they do.
|
||||
|
||||
Examples:
|
||||
|
||||
```scss
|
||||
.text-white { color: #fff !important; }
|
||||
|
||||
.bg-gray-light { background-color: #ddd !important; }
|
||||
|
||||
.mr-1 { margin-right: $spacer !important; }
|
||||
|
||||
.d-inline-block { display: inline-block !important; }
|
||||
|
||||
.rounded { border-radius: 3px !important; }
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Feature-specific Sass
|
||||
|
||||
It's inevitable that we'll need to write some custom styles for new features on occasion. This should only be done when implementing the design cannot be achieved with utility or component styles. Follow these guidelines when writing custom Sass:
|
||||
|
||||
* Don't override global component and utility styles.
|
||||
* Name-space to the feature at the beginning of the classname and follow with the element description.
|
||||
* Write custom styles in a way that may be folded back into the styleguide should the pattern become useful across the site, i.e. follow BEM structure for building components.
|
||||
|
||||
```scss
|
||||
// Do
|
||||
// Custom background for Git Merge ad
|
||||
.git-merge-box {
|
||||
padding: $spacer-6;
|
||||
background-color: #222;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
// Don't
|
||||
.git-merge-box {
|
||||
.boxed-group {
|
||||
padding: 48px;
|
||||
background-color: #222;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Inline styles
|
||||
|
||||
Inline styles are performant and deal with one off use cases that don't need to live in CSS. Sometimes it will make more sense to add an inline style than write a new line of CSS that will live on in our codebase longer than the markup will.
|
||||
|
||||
The most common use case is for applying widths and heights to images. Other use cases might be to apply a custom width to a div to work with it's content.
|
||||
|
||||
```html
|
||||
<!-- Image width and height -->
|
||||
<img width="20" height="20" src="https://github.com/github.png">
|
||||
|
||||
<!-- Custom width for a div that is not a repeated pattern -->
|
||||
<div class="d-table-cell py-3 pr-3" style="width: 72px">
|
||||
```
|
||||
|
||||
## Further reading
|
||||
|
||||
There's a lot happening in the world of front-end web development! To help you understand our principles better, we've put together a list articles that reflect our thinking and influenced our decisions.
|
||||
|
||||
**[About HTML semantics and front-end architecture](http://nicolasgallagher.com/about-html-semantics-front-end-architecture/)**<br/>
|
||||
This article contains a collection of thoughts on HTML semantics, components and approaches to front-end architecture, class naming patterns, and HTTP compression. Many of the concepts covered here have been adopted as best practices in modern front-end development. This is a must read!
|
||||
|
||||
**[BEMantic: DRY Like You Mean It](https://medium.com/@stowball/bemantic-dry-like-you-mean-it-133ea3843d98#.snpgwck6f)**<br/>
|
||||
A case for taking an object oriented approach to writing CSS using Sass and BEM.
|
||||
|
||||
**[The media object saves hundreds of lines of code](http://www.stubbornella.org/content/2010/06/25/the-media-object-saves-hundreds-of-lines-of-code/)**<br/>
|
||||
The media object—a block with an image on the left and content on the right—is an extremely common pattern on web pages. This article explores ways to write a flexible and versatile media object, and is a great example of how an object oriented CSS approach can help you write more reusable code.
|
||||
|
||||
**[OOCSS wiki](https://github.com/stubbornella/oocss/wiki)**<br/>
|
||||
The Object Oriented CSS framework is a collection of code that’s built using the OOCSS approach. The OOCSS approach is a way of writing CSS that’s fast, maintainable, and standards-based. This framework can help you get started.
|
||||
|
||||
**[The Importance of !important: Forcing Immutability in CSS](http://csswizardry.com/2016/05/the-importance-of-important/)**<br/>
|
||||
This article is a case for using the `!important` flag in CSS, and describes the best ways to use it.
|
||||
|
||||
**[The Specificity Graph](http://csswizardry.com/2014/10/the-specificity-graph/)**<br/>
|
||||
This is a guide to organizing your project's CSS selectors by specificity. Using the specificity graph, you can plot the health of a project's CSS and identify areas with higher-than-ideal specificity.
|
||||
|
||||
**[CSS Guidelines](http://cssguidelin.es/)**<br/>
|
||||
High-level advice and guidelines for writing sane, manageable, scalable CSS.
|
||||
|
||||
**[WTF, HTML and CSS?](http://wtfhtmlcss.com/)**<br/>
|
||||
A curated list of commonly frustrating HTML and CSS quandaries, miscues, and dilemmas.
|
||||
|
||||
**[Solved by flexbox](https://philipwalton.github.io/solved-by-flexbox/)**<br/>
|
||||
A showcase of problems once hard or impossible to solve with CSS alone, now made trivially easy with Flexbox.
|
73
docs/pages/css/principles/scss.md
Normal file
@ -0,0 +1,73 @@
|
||||
---
|
||||
title: SCSS
|
||||
---
|
||||
|
||||
{:toc}
|
||||
|
||||
## Spacing
|
||||
|
||||
* Use soft-tabs with a two space indent. Spaces are the only way to guarantee code renders the same in any person's environment.
|
||||
* Put spaces after `:` in property declarations.
|
||||
* Put spaces before `{` in rule declarations.
|
||||
* Put line breaks between rulesets.
|
||||
* When grouping selectors, keep individual selectors to a single line.
|
||||
* Place closing braces of declaration blocks on a new line.
|
||||
* Each declaration should appear on its own line for more accurate error reporting.
|
||||
|
||||
## Formatting
|
||||
|
||||
* Use hex color codes `#000` unless using `rgba()` in raw CSS (the SCSS `rgba()` function is overloaded to accept hex colors, as in `rgba(#000, .5)`).
|
||||
* Use `//` for comment blocks (instead of `/* */`).
|
||||
* Avoid specifying units for zero values, e.g., `margin: 0;` instead of `margin: 0px;`.
|
||||
* Strive to limit use of shorthand declarations to instances where you must explicitly set all the available values.
|
||||
|
||||
## Guidelines for using Sass feaures (WIP)
|
||||
_When and when not to create:_
|
||||
|
||||
* Variables
|
||||
* Mixins
|
||||
* Functions
|
||||
|
||||
## Lint rules (WIP)
|
||||
|
||||
As a rule of thumb, avoid unnecessary nesting in SCSS. At most, aim for three levels. If you cannot help it, step back and rethink your overall strategy (either the specificity needed, or the layout of the nesting).
|
||||
|
||||
* Nesting depth: 3
|
||||
* Extends: no
|
||||
* Duplicated properties: no
|
||||
* Final new lines: yes
|
||||
* Hex length: 6
|
||||
* ID selectors: no
|
||||
* Leading zero: yes
|
||||
* Naming format: lowercase-with-hyphens
|
||||
* Property order: see [HTML property order](./html)
|
||||
* Selector depth: 3
|
||||
* Single line properties: yes
|
||||
|
||||
## Examples
|
||||
|
||||
Here are some good examples that apply the above guidelines:
|
||||
|
||||
```scss
|
||||
// Example of good basic formatting practices
|
||||
.styleguide-format {
|
||||
color: #000;
|
||||
background-color: rgba(0, 0, 0, .5);
|
||||
border: 1px solid #0f0;
|
||||
}
|
||||
|
||||
// Example of individual selectors getting their own lines (for error reporting)
|
||||
.multiple,
|
||||
.classes,
|
||||
.get-new-lines {
|
||||
display: block;
|
||||
}
|
||||
|
||||
// Avoid unnecessary shorthand declarations
|
||||
.not-so-good {
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
.good {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
```
|
15
docs/pages/css/status-key.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: Status key
|
||||
---
|
||||
|
||||
import StatusLabel from '../../src/StatusLabel'
|
||||
|
||||
Primer is constantly evolving and we have many styles to refactor and bring up to standard. The status of each package is shown with it's corresponding documentation so you can be confident which styles are safe to use.
|
||||
|
||||
| Label | Description |
|
||||
| :----- | :--- |
|
||||
| <StatusLabel id="stable" status="Stable" /> | These styles are safe to use in production and there are currently no planned updates. If you find a problem with these styles please [open and issue](https://github.com/github/design-systems/issues). |
|
||||
| <StatusLabel id="new-release" status="New release" /> | These are newly shipped styles that are safe to use in production. While these styles will have been thoroughly tested before being shipped you may find room for improvements in the documentation, and it's possible you may find the occasional bug. Like all the code we ship, the best test is when lots of people start using it. We'll link to a corresponding feedback issue for new releases so you can comment if you find any problems or have questions. |
|
||||
| <StatusLabel id="experimental" status="Experimental" /> | These styles are a work in progress that are safe to use, but haven't been battle-tested yet. That means they may go through more significant changes in the near future. You can use these styles and help us test out these styles while we're working on, them as long as you are prepared for changes (though we'll handle updates as much as possible for you). We will have a corresponding issue for experimental styles where you can let us know you're using them, and can leave feedback or ask questions. |
|
||||
| <StatusLabel id="in-review" status="In review" /> | This means we are actively reviewing these styles with a view to refactor or deprecate them. They are still safe to use in production but if you are considering using them on a new feature it would be good to talk to us so we can make sure that our review doesn't disrupt your work. All styles in review will have a corresponding issue explaining why they are in review and will link to any relevant pull requests. |
|
||||
| <StatusLabel id="deprecated" status="Deprecated" /> | These styles are in the process of being removed and should no longer be used in production. The design systems team are responsible for updating existing views that currently use these styles, however you can help us by replacing deprecated classes from views that you are working on (if you have the time). You will get a lint error telling you to replace these styles if you attempt to add new instances. Please contact us if this causes you a problem. |
|
23
docs/pages/css/support/color-system.md
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: Color system
|
||||
description: 'Sass variables, mixins, and functions for use in our components.'
|
||||
source: 'https://github.com/primer/primer/blob/master/modules/primer-support/lib/variables/color-system.scss'
|
||||
symbols: [gray, grey, blue, green, purple, yellow, orange, red, black, white]
|
||||
status: Stable
|
||||
status_issue: 'https://github.com/github/design-systems/issues/301'
|
||||
package:
|
||||
name: primer-support
|
||||
---
|
||||
|
||||
import {BorderBox, Box, Flex, Heading, Text} from '@primer/components'
|
||||
import {ColorPalette, ColorVariables} from '../../../src/color-system'
|
||||
|
||||
{:toc}
|
||||
|
||||
## Color palette
|
||||
|
||||
<ColorPalette />
|
||||
|
||||
## Color variables
|
||||
|
||||
<ColorVariables />
|
53
docs/pages/css/tools/atom-packages.md
Normal file
@ -0,0 +1,53 @@
|
||||
---
|
||||
title: Atom packages
|
||||
---
|
||||
|
||||
[Atom editor](https://atom.io/) is a _"A hackable text editor for the 21st Century"_ built by GitHub. If you use it as your editor of choice, the design systems team has a list of packages that we find useful for CSS development.
|
||||
|
||||
We keep a list of suggested packages in our [atom-packages repository](https://github.com/primer/atom-packages). To install all packages, run this command in your terminal.
|
||||
|
||||
```sh
|
||||
apm install $(curl -L https://raw.githubusercontent.com/primer/atom-packages/master/packages)
|
||||
```
|
||||
|
||||
{:toc}
|
||||
|
||||
## Autocomplete Primer
|
||||
|
||||
The [Autocomplete Primer package](https://atom.io/packages/autocomplete-primer) is a custom [Primer](https://github.com/primer) package that autocompletes class names for [utilities](/css/utilities) and Rails helper tags for [Octicons](https://github.com/primer/octicons/tree/master/lib/octicons_helper#readme).
|
||||
|
||||
Install [autocomplete-primer](https://atom.io/packages/autocomplete-primer) using Atom's package manager or enter this into your terminal:
|
||||
|
||||
```sh
|
||||
apm install autocomplete-primer
|
||||
```
|
||||
|
||||
## Color picker
|
||||
|
||||
[Color picker](https://atom.io/packages/color-picker) adds a color picking overlay with sliders and color wheels.
|
||||
|
||||
Install [color-picker](https://atom.io/packages/color-picker) using Atom's package manager or enter this into your terminal:
|
||||
|
||||
```sh
|
||||
apm install color-picker
|
||||
```
|
||||
|
||||
## Pigments
|
||||
|
||||
[Pigments](https://atom.io/packages/pigments) adds corresponding backgrounds to CSS color values in the editor for higher visibility. It also scans the source files for color variables and displays the true value.
|
||||
|
||||
Install [pigments](https://atom.io/packages/pigments) using Atom's package manager or enter this into your terminal:
|
||||
|
||||
```sh
|
||||
apm install pigments
|
||||
```
|
||||
|
||||
## Stylelint linter
|
||||
|
||||
We use [Stylelint](https://stylelint.io/), an extension to the [Atom linter package](https://atom.io/packages/linter), for [linting our SCSS](/css/tools/linting). To prevent linting errors during the build we recommend you install [linter-stylelint](https://atom.io/packages/linter-stylelint), which will alert you of any errors locally while you work.
|
||||
|
||||
Install [linter-stylelint](https://atom.io/packages/linter-stylelint) using Atom's package manager or enter this into your terminal:
|
||||
|
||||
```sh
|
||||
apm install linter linter-stylelint
|
||||
```
|
17
docs/pages/css/tools/docset.md
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
title: Docset
|
||||
---
|
||||
|
||||
We use and ❤️ [Kapeli's Dash app][dash] for browsing API documentation. Dash comes with 150+ offline documentation sets, but doesn't have our style guide.
|
||||
|
||||
So we've generated a [GitHub Style Guide docset][dash-feed] that gives you access to this site offline.
|
||||
|
||||
## Installation
|
||||
|
||||
1. First you'll need to make sure you have [Dash app][dash] installed.
|
||||
2. Once installed, open the app, and click this [install docset link][dash-feed].
|
||||
3. After the docset has been added to your list, click "download".
|
||||
![](https://user-images.githubusercontent.com/54012/32087284-a87ed8f4-ba8f-11e7-9d84-c61913336491.png)
|
||||
|
||||
[dash]: https://kapeli.com/dash
|
||||
[dash-feed]: dash-feed://https%3A%2F%2Fstyleguide.github.com%2Ffeeds%2FGitHub%20Style%20Guide.xml
|
6
docs/pages/css/tools/index.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Tools
|
||||
---
|
||||
|
||||
Design and development tools for working with the GitHub CSS toolkit.
|
||||
|
104
docs/pages/css/tools/linting.md
Normal file
@ -0,0 +1,104 @@
|
||||
---
|
||||
title: Linting
|
||||
---
|
||||
|
||||
We use linters to enforce [coding principles and standards](/css/principles). On every pull request we run the linters on the code to make sure any changes meet our standards. When a commit contains code that doesn't meet the standards, the build fails which blocks merging into master and deploying to production.
|
||||
|
||||
For teams working on `github/github` this configuration is all setup for you. While we do recommend you also [setup a plugin](#plugins) in your editor, this is not required.
|
||||
|
||||
For everyone else we encourage you to adopt all or some of these tools in your workflow.
|
||||
|
||||
{:toc}
|
||||
|
||||
## CSS
|
||||
|
||||
We use [stylelint](http://stylelint.io/) to lint our CSS. If you are working on `github/github` you don't need to do any setup because stylelint comes bundled within the repository. If you are working on other properties, you may need to setup stylelint. You can install stylelint via npm:
|
||||
|
||||
```
|
||||
npm install -g stylelint
|
||||
```
|
||||
|
||||
Whether you work on `github/github` or not, it's useful to see lint errors locally. The easiest way to lint your code is to install a [plugin](#plugins) in your workflow. If you prefer to run stylelint manually, pass it a glob pattern of the files you want to lint. If you work on `github/github`, you can run stylelint from the command line:
|
||||
|
||||
```
|
||||
bin/stylelint "app/assets/stylesheets/**/*.scss" --syntax scss
|
||||
```
|
||||
|
||||
For more advanced usage, we recommend reading the [stylelint user guide](http://stylelint.io/user-guide/) and checking out our [primer stylelint configuration](https://github.com/primer/primer/tree/master/tools/stylelint-config-primer).
|
||||
|
||||
### Configuration
|
||||
|
||||
Stylelint is [configured by a JSON file](http://stylelint.io/user-guide/configuration/) that specifies which linter rules we enforce. If you are working on `github/github`, you don't need to do any setup because the configuration is included within the repository bundle.
|
||||
|
||||
We have extracted out our configuration file into a separate repository [primer/stylelint-config-primer](https://github.com/primer/primer/tree/master/tools/stylelint-config-primer). This gives us a central source where we can keep the configuration up-to-date and distribute easily across all our projects.
|
||||
|
||||
To [use the configuration](https://github.com/primer/primer/tree/master/tools/stylelint-config-primer#usage) in your project, install the config `npm install --save stylelint-config-primer` via npm, and extend the config in your `.stylelintrc` file. Put the file in the root directory of your project.
|
||||
|
||||
```json
|
||||
{
|
||||
"extends": "stylelint-config-primer"
|
||||
}
|
||||
```
|
||||
|
||||
A list of all the specific rules we have enabled are documented in [the package README](https://www.npmjs.com/package/stylelint-config-primer#documentation).
|
||||
|
||||
### Disabling
|
||||
|
||||
We use linters to help ensure our CSS is written according to our principles. On occasion it's necessary to break those principles and disable a linter. This is usually because of one of the following scenarios:
|
||||
|
||||
- There is no other way to achieve the desired style due to other code limitations, such as the way the views are rendered or the way the content is output (such as rendered markdown).
|
||||
- There is no other way to achieve the desired style due to conflicts with existing styles that would require considerable effort to fix.
|
||||
- Breaking a lint rule offers an improvement to code readability and/or maintainability (this is often the case when writing components and supporting SCSS, such as mixins and functions).
|
||||
|
||||
To disable a linter, place a `// stylelint-disable` comment around the specific block of CSS that you need to override. For example:
|
||||
|
||||
```scss
|
||||
// When a branch name is a link
|
||||
// stylelint-disable selector-no-qualifying-type
|
||||
a.branch-name { color: $brand-blue; }
|
||||
// stylelint-enable selector-no-qualifying-type
|
||||
```
|
||||
|
||||
#### If you choose to disable a linting rule...
|
||||
|
||||
1. You'll need to specify a reason for disabling the lint.
|
||||
2. Say what lint you're disabling, this example has `selector-no-qualifying-type`.
|
||||
3. Make sure to `disable` then `enable` the lint again after your code block. This ensures that the rule is re-enabled for any code after the block in question.
|
||||
|
||||
When you disable a linter on GitHub, [Sentinel](https://github.com/github/sentinel) will post a comment on your pull request notifying the Design Systems team of this override. This is because disabled lints act as a signal that there may be issues with Primer and/or the CSS in question.
|
||||
|
||||
While linters shouldn't be disabled without consideration, essentially they act as a guide to how we write CSS rather than steadfast rules. For more info on disabling configuration, visit the [stylelint docs](http://stylelint.io/user-guide/configuration/#turning-rules-off-from-within-your-css).
|
||||
|
||||
### Plugins
|
||||
|
||||
Our linter runs on every commit, but finding you have a lint error after a push can be frustrating and waste time. To avoid this, a [collection of editor plugins](http://stylelint.io/user-guide/complementary-tools/) are available to help you lint while you code..
|
||||
|
||||
The Design Systems team recommends using [Atom](https://atom.io/) with the [linter-stylelint](https://github.com/AtomLinter/linter-stylelint) package installed.
|
||||
|
||||
![image](https://cloud.githubusercontent.com/assets/54012/21456489/6a10b370-c8f6-11e6-9199-bb3709e79794.png)
|
||||
|
||||
We also have a [list of Atom packages](/css/tools/atom-packages) that we find useful for development.
|
||||
|
||||
## HTML
|
||||
|
||||
A custom Rails test called `styles_match_markup_test.rb` runs for our `.html.erb` files and checks for unused selectors. The test collects a list of all the class names used in our markup, JavaScript and documentation, then compares that with a list of all our class names in our `.scss` files.
|
||||
|
||||
This test isn't smart enough to know about CSS inheritance, but will help you catch any dead CSS in your markup or styles. Run the test locally with this command.
|
||||
|
||||
```
|
||||
bin/testrb test/fast/linting/styles_match_markup_test.rb
|
||||
```
|
||||
|
||||
Sometimes you will have generated class names that the test misses. Adding these class names to the regular expression enabled `selectors_match_markup_whitelist.yml` file will quiet the tests.
|
||||
|
||||
## Files
|
||||
|
||||
We enforce `.scss` file naming to be lowercase, only use `-` hyphens and no leading `_` underscores. Removing underscores from the files is an anti-pattern for Sass, but we're doing this because we're proactively moving away from Sass to match CSS web standards.
|
||||
|
||||
## Octicons
|
||||
|
||||
We test for the proper use of the [Octicons helper](https://github.com/primer/octicons/tree/master/lib/octicons_helper#readme) in our templates and make sure that the symbols used are valid octicons.
|
||||
|
||||
## IE rule limit
|
||||
|
||||
We check that our compiled CSS assets don't contain more selectors than the [IE CSS selector limits](https://blogs.msdn.microsoft.com/ieinternals/2011/05/14/stylesheet-limits-in-internet-explorer/).
|
95
docs/pages/css/tools/local-primer.md
Normal file
@ -0,0 +1,95 @@
|
||||
---
|
||||
title: Local development
|
||||
internal: true
|
||||
---
|
||||
|
||||
When you are working with the `github/github` codebase, you can link Primer modules with your local development environment using the Primerize script. This will allow you to make changes to primer and see them reflected on `github.localhost` without the overhead of pulling in alpha releases of a package.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Working with `github/github` on the latest codebase.
|
||||
2. Cloned down latest version of [primer/primer](https://github.com/primer/primer).
|
||||
3. The `github/github` folder and the `primer/primer` folder must share the same parent folder.
|
||||
|
||||
**Example:**
|
||||
```
|
||||
~/codefolder/
|
||||
├── github
|
||||
└── primer
|
||||
```
|
||||
|
||||
## Linking to your local primer repository
|
||||
|
||||
In your terminal start the server with the environment variable `LOCAL_PRIMER=1`. For example.
|
||||
|
||||
```
|
||||
> LOCAL_PRIMER=1 script/server
|
||||
```
|
||||
|
||||
When the variable is present, the script will check for linked local Primer packages. If it's not linked, then it will proceed to link the primer packages in `../primer` to your GitHub application. When the server starts with successfully linked packages, you will see a clear message.
|
||||
|
||||
**Example output:**
|
||||
|
||||
```
|
||||
❯ LOCAL_PRIMER=1 script/server
|
||||
--------------------------------------------------------------------------------
|
||||
Development Primer is linked to the local Primer repository found here
|
||||
/Users/usera/github/primer
|
||||
--------------------------------------------------------------------------------
|
||||
14:09:14 web.1 | started with pid 36981
|
||||
14:09:14 assets.1 | started with pid 36982
|
||||
14:09:14 longpoll.1 | started with pid 36984
|
||||
14:09:14 git-daemon.1 | started with pid 36986
|
||||
14:09:14 gitauth.1 | started with pid 36988
|
||||
14:09:14 codeload.1 | started with pid 36991
|
||||
14:09:14 babeld.1 | started with pid 36996
|
||||
```
|
||||
|
||||
## Resetting github/github development assets
|
||||
|
||||
To turn off Primerize, rerun `script/server` without the `LOCAL_PRIMER` flag. The script checks if you are still linked and restores everything to normal.
|
||||
|
||||
**Example output:**
|
||||
|
||||
```
|
||||
❯ script/server
|
||||
--------------------------------------------------------------------------------
|
||||
Development Primer is linked to the local Primer repository found here
|
||||
/Users/usera/github/primer
|
||||
--------------------------------------------------------------------------------
|
||||
Turning off linked primer
|
||||
extracting primer-alerts@1.5.5.tgz
|
||||
extracting primer-avatars@1.5.2.tgz
|
||||
extracting primer-base@1.7.0.tgz
|
||||
extracting primer-blankslate@1.4.5.tgz
|
||||
extracting primer-box@2.5.5.tgz
|
||||
extracting primer-branch-name@1.0.3.tgz
|
||||
extracting primer-breadcrumb@1.5.1.tgz
|
||||
extracting primer-buttons@2.5.3.tgz
|
||||
extracting primer-core@6.8.0.tgz
|
||||
extracting primer-forms@2.1.0.tgz
|
||||
extracting primer-labels@1.5.5.tgz
|
||||
extracting primer-layout@1.4.5.tgz
|
||||
extracting primer-markdown@3.7.5.tgz
|
||||
extracting primer-marketing-buttons@1.0.4.tgz
|
||||
extracting primer-marketing-support@1.5.0.tgz
|
||||
extracting primer-marketing-type@1.4.5.tgz
|
||||
extracting primer-marketing-utilities@1.6.1.tgz
|
||||
extracting primer-marketing@6.2.0.tgz
|
||||
extracting primer-navigation@1.5.3.tgz
|
||||
extracting primer-page-headers@1.4.5.tgz
|
||||
extracting primer-page-sections@1.4.5.tgz
|
||||
extracting primer-popover@0.0.6.tgz
|
||||
extracting primer-product@5.6.2.tgz
|
||||
extracting primer-subhead@1.0.3.tgz
|
||||
extracting primer-support@4.5.2.tgz
|
||||
extracting primer-table-object@1.4.5.tgz
|
||||
extracting primer-tables@1.4.5.tgz
|
||||
extracting primer-tooltips@1.5.3.tgz
|
||||
extracting primer-truncate@1.4.5.tgz
|
||||
extracting primer-utilities@4.8.5.tgz
|
||||
extracting primer@10.4.0.tgz
|
||||
regenerating node binstubs
|
||||
14:13:17 web.1 | started with pid 38585
|
||||
14:13:17 assets.1 | started with pid 38586
|
||||
```
|
35
docs/pages/css/tools/prototyping.md
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
title: Prototyping
|
||||
---
|
||||
|
||||
You're welcome to use whatever prototyping tool suits your needs, however we've set up some options that will help you get started quickly.
|
||||
|
||||
The power of prototyping in code is that you can create clickable mocks that can be shared via a URL. This can be useful for exploring designs and interactions or for user research sessions. Prototypes can be throw-away, or part of your process for building out new features since you can work with the same CSS we use in production.
|
||||
|
||||
## Simple HTML prototype with Primer
|
||||
Copy the code below and paste it in a HTML file. The CDN link is always linked to the most up to date version of Primer and includes all of the modules in the core, product, and marketing packages.
|
||||
|
||||
This method requires no dev environment set up and is useful for when you want to create simple prototypes using Primer.
|
||||
|
||||
```
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<HEAD>
|
||||
<title> </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://unpkg.com/primer/build/build.css">
|
||||
</HEAD>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Jekyll prototyping with GitHub CSS and JavaScript
|
||||
The [Jekyll](http://jekyllrb.com) based prototyping tool pulls in all of GitHub's CSS, which includes all the Primer modules as well as custom CSS modules. It includes GitHub JavaScript and octicons too.
|
||||
|
||||
This tool is useful for when you want to build a more complex prototype with multiple pages, interactions and flows, or need to work with GitHub CSS. You can take advantage of everything you get with [Jekyll](http://jekyllrb.com/docs/home/), such as layout templates, includes, and collections.
|
||||
|
||||
The [prototype](https://ghe.io/github/prototype) tool lives on our enterprise install. All you need to do is fork the repository to your account and [follow the instructions](https://ghe.io/github/prototype#basics) for publishing.
|
||||
|
||||
Having the repository on our enterprise install has the added benefit of making the mocks password protected, but they don't have to be. If you want to publish something publicly you can change configuration and publish to github.com.
|
20
docs/pages/css/tools/sketch-templates.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: Sketch templates
|
||||
internal: true
|
||||
---
|
||||
|
||||
We often use Sketch for mocking up designs before coding them. To make this process faster and to keep our designs consistent, we have created UI kits that contain many of our commonly used styles.
|
||||
|
||||
## Product UI Kits
|
||||
|
||||
[The Product UI Kit](https://github.com/github/design/blob/master/resources/sketch/github-ui-kit.sketch) is a collection of our core GitHub UI styles and components suitable for building mockups. It also includes a starter page template with a site and repo header.
|
||||
|
||||
Don't waste time manually updating the Octicons artboard when icons are added, removed, or changed. See the [Octicons Sketch plugin](https://github.com/github/design/tree/master/resources/sketch/octicons-plugin) directory for a way to automate those updates.
|
||||
|
||||
![thumbnail of sketch UI kit](https://cloud.githubusercontent.com/assets/98681/9478261/7b4bd916-4b2b-11e5-991f-3bbef3f4c9a6.png)
|
||||
|
||||
## Email templates
|
||||
|
||||
[This email templates](https://github.com/github/design/blob/master/resources/sketch/email-templates.sketch) are a collection of current GitHub email templates which serve as a reference for new email designs.
|
||||
|
||||
![thumbnail of sketch email templates](https://cloud.githubusercontent.com/assets/1319791/22992477/cb5fcb5e-f38d-11e6-9549-449018f31153.png)
|
19
docs/pages/css/tools/testing.md
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
title: Testing resources
|
||||
---
|
||||
|
||||
At GitHub we use a staging environment called review-lab for testing with production data. Deploying a branch to [review-lab](https://github.com/github/github/blob/master/docs/deployment.md#test-in-lab-environments) for testing is one of the best ways to test your work with real production data prior to an actual production deployment.
|
||||
|
||||
To make testing on review-lab for extended periods of time easier, you can use the [Redirector](https://chrome.google.com/webstore/detail/redirector/pajiegeliagebegjdhebejdlknciafen) extension for Chrome to redirect all GitHub.com traffic to your review-lab deploy URL.
|
||||
|
||||
## How to use it
|
||||
|
||||
1. Install the [extension](https://chrome.google.com/webstore/detail/redirector/pajiegeliagebegjdhebejdlknciafen)
|
||||
2. Open the extension options panel from the icon in Chrome
|
||||
3. In the "From" field, enter: `https:\/\/github\.com\/?(.+)?`
|
||||
4. In the "To" field, enter: `https://branchname.review-lab.github.com/$1` where `branchname` is the name of your branch.
|
||||
|
||||
## Tips
|
||||
|
||||
- You'll need to remove your rule or simply disable the extension when you want to stop redirecting to your branch.
|
||||
- If you're trying to reach GitHub.com or your review-lab URL but are getting a "This page isn't working" message in your browser, that means your review-lab deploy has gone stale and needs to be redeployed.
|
250
docs/pages/css/utilities/colors.md
Normal file
@ -0,0 +1,250 @@
|
||||
---
|
||||
title: Colors
|
||||
description: 'Immutable, atomic CSS classes to rapidly build product'
|
||||
status: Stable
|
||||
package:
|
||||
name: primer-utilities
|
||||
source: 'https://github.com/primer/primer/tree/master/modules/primer-utilities/lib/colors.scss'
|
||||
status_issue: 'https://github.com/github/design-systems/issues/97'
|
||||
---
|
||||
|
||||
import colors from 'primer-colors'
|
||||
import {Box} from '@primer/components'
|
||||
const Swatch = props => <Box mt={2} height={60} {...props} />
|
||||
|
||||
Use color utilities to apply color to the background of elements, text, and borders.
|
||||
|
||||
* [Background colors](#background-colors)
|
||||
* [Text colors](#text-colors)
|
||||
* [Link colors](#link-colors)
|
||||
* [Border colors](#border-colors)
|
||||
|
||||
## Background colors
|
||||
|
||||
Background colors are most commonly used for filling large blocks of content or areas with a color. When selecting a background color, make sure the foreground color contrast passes a minimum WCAG accessibility rating of [level AA](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html). Meeting these standards ensures that content is accessible by everyone, regardless of disability or user device. You can [check your color combination with this demo site](http://jxnblk.com/colorable/demos/text/). For more information, read our [accessibility standards](../principles/accessibility).
|
||||
|
||||
### Gray
|
||||
|
||||
<div class="container-lg clearfix mb-4">
|
||||
<div class="col-3 float-left pr-4">
|
||||
<div class="h4">.bg-gray</div>
|
||||
<code>{colors.gray[1]}, $bg-gray</code>
|
||||
<Swatch className="bg-gray" />
|
||||
</div>
|
||||
<div class="col-9 float-left">
|
||||
<div class="container-lg clearfix">
|
||||
<div class="col-6 float-left">
|
||||
<div class="h4">.bg-gray-dark</div>
|
||||
<code>{colors.gray[9]}, $bg-gray-dark</code>
|
||||
<Swatch className="bg-gray-dark border-right-0" />
|
||||
</div>
|
||||
<div class="col-6 float-left">
|
||||
<div class="h4">.bg-gray-light</div>
|
||||
<code>{colors.gray[0]}, $bg-gray-light</code>
|
||||
<Swatch className="bg-gray-light" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
### Blue
|
||||
|
||||
<div class="container-lg clearfix mb-4">
|
||||
<div class="col-3 float-left pr-4">
|
||||
<div class="h4">.bg-blue</div>
|
||||
<code>{colors.blue[5]}, $bg-blue</code>
|
||||
<Swatch className="bg-blue" />
|
||||
</div>
|
||||
<div class="col-9 float-left">
|
||||
<div class="container-lg clearfix">
|
||||
<div class="h4">.bg-blue-light</div>
|
||||
<code>{colors.blue[0]}, $bg-blue-light</code>
|
||||
<Swatch className="bg-blue-light" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
### Yellow
|
||||
|
||||
<div class="container-lg clearfix mb-4">
|
||||
<div class="col-3 float-left pr-4">
|
||||
<div class="h4">.bg-yellow</div>
|
||||
<code>{colors.yellow[5]}, $bg-yellow</code>
|
||||
<Swatch className="bg-yellow" />
|
||||
</div>
|
||||
<div class="col-9 float-left">
|
||||
<div class="container-lg clearfix">
|
||||
<div class="h4">.bg-yellow-light</div>
|
||||
<code>{colors.yellow[2]}, $bg-yellow-light</code>
|
||||
<Swatch className="bg-yellow-light" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
### Red
|
||||
|
||||
<div class="container-lg clearfix mb-4">
|
||||
<div class="col-3 float-left pr-4">
|
||||
<div class="h4">.bg-red</div>
|
||||
<code>{colors.red[5]}, $bg-red</code>
|
||||
<Swatch className="bg-red" />
|
||||
</div>
|
||||
<div class="col-9 float-left">
|
||||
<div class="container-lg clearfix">
|
||||
<div class="h4">.bg-red-light</div>
|
||||
<code>{colors.red[1]}, $bg-red-light</code>
|
||||
<Swatch className="bg-red-light" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
### Green
|
||||
|
||||
<div class="container-lg clearfix mb-4">
|
||||
<div class="col-3 float-left pr-4">
|
||||
<div class="h4">.bg-green</div>
|
||||
<code>{colors.green[5]}, $bg-green</code>
|
||||
<Swatch className="bg-green" />
|
||||
</div>
|
||||
<div class="col-9 float-left">
|
||||
<div class="container-lg clearfix">
|
||||
<div class="h4">.bg-green-light</div>
|
||||
<code>{colors.green[1]}, $bg-green-light</code>
|
||||
<Swatch className="bg-green-light" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
### Purple
|
||||
|
||||
<div class="container-lg clearfix mb-4">
|
||||
<div class="col-3 float-left pr-4">
|
||||
<div class="h4">.bg-purple</div>
|
||||
<code>{colors.purple[5]}, $bg-purple</code>
|
||||
<Swatch className="bg-purple" />
|
||||
</div>
|
||||
<div class="col-9 float-left">
|
||||
<div class="container-lg clearfix">
|
||||
<div class="h4">.bg-purple-light</div>
|
||||
<code>{colors.purple[0]}, $bg-purple-light</code>
|
||||
<Swatch className="bg-purple-light" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Text colors
|
||||
|
||||
Use text color utilities to set text or [Octicons](https://octicons.github.com) to a specific color. Color contrast must pass a minimum WCAG accessibility rating of [level AA](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html). This ensures that viewers who cannot see the full color spectrum are able to read the text. To customize outside of the suggested combinations below, we recommend using this [color contrast testing tool](http://jxnblk.com/colorable/demos/text/). For more information, read our [accessibility standards](../principles/accessibility).
|
||||
|
||||
These are our most common text with background color combinations. They don't all pass accessibility standards currently, but will be updated in the future. **Any of the combinations with a warning icon must be used with caution**.
|
||||
|
||||
### Text color inheritance
|
||||
|
||||
You can set the color inheritance on an element by using the `text-inherit` class.
|
||||
|
||||
```html
|
||||
<div class="text-purple">
|
||||
This text is purple, <a href="#" class="text-inherit">including the link</a>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Text on white background
|
||||
|
||||
```html
|
||||
<div class="text-blue mb-2">
|
||||
.text-blue on white
|
||||
</div>
|
||||
<div class="text-gray-dark mb-2">
|
||||
.text-gray-dark on white
|
||||
</div>
|
||||
<div class="text-gray mb-2">
|
||||
.text-gray on white
|
||||
</div>
|
||||
<div class="text-red mb-2">
|
||||
.text-red on white
|
||||
</div>
|
||||
<div class="text-orange mb-2">
|
||||
.text-orange on white
|
||||
</div>
|
||||
<span class="float-left text-red tooltipped tooltipped-n" aria-label="Does not meet accessibility standards"><%= octicon("alert") %></span>
|
||||
<div class="text-orange-light mb-2">
|
||||
.text-orange-light on white
|
||||
</div>
|
||||
<span class="float-left text-red tooltipped tooltipped-n" aria-label="Does not meet accessibility standards"><%= octicon("alert") %></span>
|
||||
<div class="text-green mb-2 ml-4">
|
||||
.text-green on white
|
||||
</div>
|
||||
<div class="text-purple mb-2">
|
||||
.text-purple on white
|
||||
</div>
|
||||
```
|
||||
|
||||
### Text on colored backgrounds
|
||||
|
||||
```html
|
||||
<div class="text-white bg-blue mb-2">
|
||||
.text-white on .bg-blue
|
||||
</div>
|
||||
<div class="bg-blue-light mb-2">
|
||||
.text-gray-dark on .bg-blue-light
|
||||
</div>
|
||||
<div class="text-white bg-red mb-2">
|
||||
.text-white on .bg-red
|
||||
</div>
|
||||
<div class="text-red bg-red-light mb-2">
|
||||
.text-red on .bg-red-light
|
||||
</div>
|
||||
<div class="bg-green-light mb-2">
|
||||
.text-gray-dark on .bg-green-light
|
||||
</div>
|
||||
<div class="bg-yellow mb-2">
|
||||
.text-gray-dark on .bg-yellow
|
||||
</div>
|
||||
<div class="bg-yellow-light mb-2">
|
||||
.text-gray-dark on .bg-yellow-light
|
||||
</div>
|
||||
<div class="text-white bg-purple mb-2">
|
||||
.text-white on .bg-purple
|
||||
</div>
|
||||
<div class="text-white bg-gray-dark mb-2">
|
||||
.text-white on .bg-gray-dark
|
||||
</div>
|
||||
<div class="bg-gray">
|
||||
.text-gray-dark on .bg-gray
|
||||
</div>
|
||||
```
|
||||
|
||||
## Link colors
|
||||
|
||||
Base link styles turn links blue and apply an underline on hover. You can override the base link styles with text color utilities and the following link utilities. **Bear in mind that link styles are easier for more people to see and interact with when the changes in styles do not rely on color alone.**
|
||||
|
||||
Use `link-gray` to turn the link color to `$text-gray` and remain hover on blue.
|
||||
|
||||
```html
|
||||
<a class="link-gray" href="#url">link-gray</a>
|
||||
```
|
||||
|
||||
Use `link-gray-dark` to turn the link color to `$text-gray-dark` and remain hover on blue.
|
||||
|
||||
```html
|
||||
<a class="link-gray-dark" href="#url">link-gray-dark</a>
|
||||
```
|
||||
|
||||
Use `.muted-link` to turn the link light gray in color, and blue on hover or focus with no underline.
|
||||
|
||||
```html
|
||||
<a class="muted-link" href="#url">muted-link</a>
|
||||
```
|
||||
|
||||
Use `link-hover-blue` to make any text color used with links to turn blue on hover. This is useful when you want only part of a link to turn blue on hover.
|
||||
|
||||
```html
|
||||
<a class="text-gray-dark no-underline" href="#url">
|
||||
A link with only part of it is <span class="link-hover-blue">blue on hover</span>.
|
||||
</a>
|
||||
```
|
||||
|
||||
## Border colors
|
||||
|
||||
Border colors are documented on the [border utilities page](../utilities/borders#border-width-style-and-color-utilities).
|
8
docs/pages/css/utilities/index.md
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
title: Utilities
|
||||
---
|
||||
|
||||
Utilities provide the building blocks for layout and handle a range common use cases that help us avoid writing custom styles. When we need to add some margin or padding, rather than adding a new selector specific to that use case, we can use utilities. This helps us reduce the number of unique property-value pairs, and helps us keep more consistent styles across the site.
|
||||
|
||||
* Utilities should do one job well and one job only, are immutable, and on occasion are an acceptable approach to overriding component styles.
|
||||
* Utility class-names should be transparent and clearly describe the job they do.
|
2
docs/pages/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
import redirect from '../src/redirect'
|
||||
export default redirect('/css')
|
1
docs/pages/robots.txt
Normal file
@ -0,0 +1 @@
|
||||
User-agent: *
|
1
docs/prettier.config.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = require('eslint-plugin-github/prettier.config')
|
157
docs/script/check-links
Executable file
@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env node
|
||||
const spinner = require('char-spinner')
|
||||
const {gray, green, yellow, red, bold} = require('colorette')
|
||||
const {SiteChecker} = require('broken-link-checker')
|
||||
|
||||
const yargs = require('yargs')
|
||||
.option('excluded-schemes', {type: String, alias: 's', default: ['dash-feed', 'mailto']})
|
||||
.option('filter-level', {type: Number, alias: 'L', default: 3})
|
||||
.option('max-sockets-per-host', {type: Number, alias: 'm', default: 1})
|
||||
.option('verbose', {type: Boolean, alias: 'v', default: false})
|
||||
|
||||
const options = yargs.argv
|
||||
|
||||
const args = options._
|
||||
const VERBOSE = options.verbose
|
||||
|
||||
const pages = []
|
||||
const seen = new Set()
|
||||
const excepted = new Map()
|
||||
|
||||
const OK = ' ✓ '
|
||||
const NOT_OK = ' ✘ '
|
||||
const TAG_LENGTH = 3
|
||||
|
||||
const URL = args[0]
|
||||
if (URL) {
|
||||
log(green('go!'), bold(URL))
|
||||
} else {
|
||||
log('err', 'you must provide a URL')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
let page = {url: URL, links: []}
|
||||
|
||||
const exceptions = {
|
||||
'GitHub private repo': url =>
|
||||
[
|
||||
// this is a list of known GitHub private repos
|
||||
'https://github.com/github/accessibility',
|
||||
'https://github.com/github/design',
|
||||
'https://github.com/github/design-systems',
|
||||
'https://github.com/github/github',
|
||||
'https://github.com/github/sentinel'
|
||||
].some(repo => url.startsWith(repo))
|
||||
}
|
||||
|
||||
const checker = new SiteChecker(options, {
|
||||
page(error, url) {
|
||||
if (error) {
|
||||
log(red('ERR'), `${url} (${error.code})`)
|
||||
} else if (page) {
|
||||
const {url, response, links = []} = page
|
||||
const num = String(links.length).padEnd(TAG_LENGTH)
|
||||
let message = `${bold(num)}${pages.length ? ' unique' : ''} links`
|
||||
if (!VERBOSE) message = `${message} on ${yellow(url)}`
|
||||
log(OK, message)
|
||||
if (links.length) {
|
||||
pages.push(page)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
html(tree, robots, response, url) {
|
||||
if (VERBOSE) log(yellow('get'), url)
|
||||
page = {url, links: []}
|
||||
},
|
||||
|
||||
junk(result) {
|
||||
const url = result.url.resolved || result.url.original
|
||||
if (!url || seen.has(url)) {
|
||||
return
|
||||
} else if (VERBOSE) {
|
||||
log(' '.repeat(TAG_LENGTH), gray(`skip ${shorten(url)}`))
|
||||
} else if (result.excluded && url.indexOf(URL) !== 0) {
|
||||
log(yellow('---'), gray(`excluded: ${url}`))
|
||||
}
|
||||
seen.add(url)
|
||||
},
|
||||
|
||||
link(result) {
|
||||
const url = result.url.resolved || result.url.original
|
||||
if (VERBOSE && !seen.has(url)) log(' + ', gray('link'), shorten(url))
|
||||
|
||||
for (const [reason, test] of Object.entries(exceptions)) {
|
||||
if (test(url)) {
|
||||
log(yellow('---'), gray(`skip ${url}`), yellow(reason))
|
||||
excepted.set(url, reason)
|
||||
seen.add(url)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!seen.has(url)) page.links.push(result)
|
||||
seen.add(url)
|
||||
},
|
||||
|
||||
end() {
|
||||
const allBroken = []
|
||||
for (const page of pages) {
|
||||
const broken = page.links.filter(link => link.broken)
|
||||
allBroken.push(...broken)
|
||||
|
||||
if (!broken.length && !VERBOSE) {
|
||||
continue
|
||||
} else {
|
||||
const num = broken.length ? red(` ${broken.length}`.padEnd(TAG_LENGTH)) : green(' 0').padEnd(TAG_LENGTH)
|
||||
log(bold(num), `broken links on ${bold(page.url)}`)
|
||||
}
|
||||
|
||||
for (const link of page.links) {
|
||||
if (!link.broken && !VERBOSE) continue
|
||||
const tag = link.broken ? red(NOT_OK) : green(OK)
|
||||
const reason = link.broken ? link.brokenReason.replace(/HTTP_/, '') : ''
|
||||
const url = link.url.resolved || link.url.original
|
||||
log(tag, shorten(url), yellow(reason))
|
||||
link.source = url
|
||||
link.reason = reason
|
||||
}
|
||||
log('')
|
||||
}
|
||||
|
||||
if (excepted.size) {
|
||||
log(yellow(OK), `Excepted ${excepted.size} links:`)
|
||||
const exceptedURLs = Array.from(excepted.keys()).sort()
|
||||
for (const url of exceptedURLs) {
|
||||
log(yellow(OK), `${yellow(excepted.get(url))} ${gray(url)}`)
|
||||
}
|
||||
log('')
|
||||
}
|
||||
|
||||
if (allBroken.length) {
|
||||
log(red(NOT_OK), `${red(allBroken.length)} broken links:`)
|
||||
for (const link of allBroken) {
|
||||
log(red(NOT_OK), red(link.reason), link.url.original, gray('from'), shorten(link.source))
|
||||
}
|
||||
log('')
|
||||
process.exitCode = 1
|
||||
} else {
|
||||
log(green(OK), bold('0'), 'broken links')
|
||||
}
|
||||
|
||||
log('')
|
||||
}
|
||||
})
|
||||
|
||||
spinner()
|
||||
checker.enqueue(URL)
|
||||
checker.resume()
|
||||
|
||||
function log(tag, ...args) {
|
||||
spinner.clear()
|
||||
console.log(tag ? gray(`[${tag}]`) : '', ...args)
|
||||
}
|
||||
|
||||
function shorten(url) {
|
||||
return String(url).indexOf(URL) === 0 ? gray(URL) + (url.substr(URL.length) || '/') : url
|
||||
}
|
65
docs/script/check-relative-links
Executable file
@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env node
|
||||
const globby = require('globby')
|
||||
const remark = require('remark-parse')
|
||||
const stringify = require('remark-stringify')
|
||||
const stringifyPosition = require('unist-util-stringify-position')
|
||||
const unified = require('unified')
|
||||
const {green, yellow, red} = require('colorette')
|
||||
const {join} = require('path')
|
||||
const {readFile} = require('fs-extra')
|
||||
const {selectAll} = require('unist-util-select')
|
||||
|
||||
const processor = unified()
|
||||
.use(remark)
|
||||
.use(stringify)
|
||||
|
||||
const checks = {
|
||||
'relative (.)': ({href}) => href.startsWith('./'),
|
||||
'relative (..)': ({href}) => href.startsWith('../'),
|
||||
'trailing slash': ({href}) => stripFragment(href).endsWith('/')
|
||||
}
|
||||
|
||||
const dir = join(__dirname, '../pages/css')
|
||||
globby(join(dir, '**/*.md'))
|
||||
.then(docs => Promise.all(docs.map(getLinks)))
|
||||
.then(docs => {
|
||||
const links = docs
|
||||
.reduce((list, doc) => {
|
||||
const path = doc.path.substr(dir.length + 1)
|
||||
return list.concat(doc.links.map(link => Object.assign({path}, link)))
|
||||
}, [])
|
||||
// exclude anchor links
|
||||
.filter(({href}) => href.charAt(0) !== '#')
|
||||
// exclude fully-qualified URLs
|
||||
.filter(({href}) => !href.match(/^https?:/))
|
||||
|
||||
for (const checkName of Object.keys(checks)) {
|
||||
const matches = links.filter(checks[checkName])
|
||||
if (matches.length) {
|
||||
console.warn(`${matches.length} link(s) with ${checkName}:`)
|
||||
for (const {path, text, href, position} of matches) {
|
||||
console.warn(` ${path} @ ${yellow(position)}: ${green(href)} ('${text}')`)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function getLinks(path) {
|
||||
return readFile(path, 'utf8')
|
||||
.then(parseLinks)
|
||||
.then(links => ({path, links}))
|
||||
}
|
||||
|
||||
function parseLinks(str) {
|
||||
const tree = processor.parse(str)
|
||||
const nodes = selectAll('link', tree)
|
||||
return nodes.map(node => ({
|
||||
href: node.url,
|
||||
text: processor.stringify({type: 'paragraph', children: node.children}).trim(),
|
||||
position: stringifyPosition(node)
|
||||
}))
|
||||
}
|
||||
|
||||
function stripFragment(url) {
|
||||
return url.replace(/#.*$/, '')
|
||||
}
|
21
docs/script/sync
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env node
|
||||
const sync = require('../lib/sync')
|
||||
const args = process.argv.slice(2)
|
||||
|
||||
const options = {
|
||||
debug: !args.includes('--quiet'),
|
||||
watch: args.includes('--watch')
|
||||
}
|
||||
|
||||
sync(options)
|
||||
.then(files => {
|
||||
const built = Object.keys(files)
|
||||
console.warn(`built ${built.length} files:`)
|
||||
for (const file of built) {
|
||||
console.warn(`- ${file}`)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error)
|
||||
process.exitCode = 1
|
||||
})
|
12
docs/script/update-assets
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
asset_path=./static
|
||||
|
||||
bundles="styleguide.js styleguide.css"
|
||||
|
||||
echo "Grabbing the latest assets from github.com..."
|
||||
for bundle in $bundles; do
|
||||
echo "Downloading $bundle -> $asset_path/github/$bundle ..."
|
||||
curl -fsSLo "$asset_path/github/$bundle" "https://github.com/site/assets/$bundle"
|
||||
done
|
8
docs/src/BoxShadow.js
Normal file
@ -0,0 +1,8 @@
|
||||
import {Box} from '@primer/components'
|
||||
import styled from 'styled-components'
|
||||
|
||||
const BoxShadow = styled(Box)`
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
`
|
||||
|
||||
export default BoxShadow
|
47
docs/src/ClipboardCopy.js
Normal file
@ -0,0 +1,47 @@
|
||||
import React from 'react'
|
||||
import {findDOMNode} from 'react-dom'
|
||||
import {Button} from '@primer/components'
|
||||
import Octicon, {Clippy} from '@githubprimer/octicons-react'
|
||||
|
||||
export default class ClipboardCopy extends React.Component {
|
||||
copy() {
|
||||
const {value = ''} = this.props
|
||||
const {clipboard} = window.navigator
|
||||
const done = () => {
|
||||
// eslint-disable-next-line react/no-find-dom-node
|
||||
findDOMNode(this).dispatchEvent(new CustomEvent('copy', {bubbles: false}))
|
||||
}
|
||||
if (clipboard) {
|
||||
return clipboard.writeText(value).then(done)
|
||||
} else if (!document.body) {
|
||||
return
|
||||
} else {
|
||||
const node = document.createElement('pre')
|
||||
node.style.width = '1px'
|
||||
node.style.height = '1px'
|
||||
node.style.position = 'fixed'
|
||||
node.style.top = '5px'
|
||||
node.textContent = value
|
||||
|
||||
const selection = window.getSelection()
|
||||
document.body.appendChild(node)
|
||||
selection.removeAllRanges()
|
||||
const range = document.createRange()
|
||||
range.selectNodeContents(node)
|
||||
selection.addRange(range)
|
||||
document.execCommand('copy')
|
||||
selection.removeAllRanges()
|
||||
document.body.removeChild(node)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {value = '', ...rest} = this.props
|
||||
return (
|
||||
<Button onClick={() => this.copy()} type="button" {...rest}>
|
||||
<Octicon icon={Clippy} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
}
|
81
docs/src/CodeExample.js
Normal file
@ -0,0 +1,81 @@
|
||||
import React from 'react'
|
||||
import HTMLtoJSX from 'html-2-jsx'
|
||||
import {Absolute, BorderBox, Box, StyledOcticon as Octicon, Relative, Text} from '@primer/components'
|
||||
import {LiveEditor, LiveError, LivePreview, LiveProvider} from 'react-live'
|
||||
import {getIconByName} from '@githubprimer/octicons-react'
|
||||
import ClipboardCopy from './ClipboardCopy'
|
||||
import Frame from './Frame'
|
||||
|
||||
import 'prism-github/prism-github.scss'
|
||||
|
||||
const LANG_PATTERN = /\blanguage-\.?(jsx?|html)\b/
|
||||
|
||||
const converter = new HTMLtoJSX({
|
||||
indent: ' ',
|
||||
createClass: false
|
||||
})
|
||||
|
||||
const defaultTransform = code => `<React.Fragment>${code}</React.Fragment>`
|
||||
|
||||
const languageTransforms = {
|
||||
html: html => defaultTransform(converter.convert(html)),
|
||||
jsx: defaultTransform
|
||||
}
|
||||
|
||||
export default function CodeExample(props) {
|
||||
const {children, dangerouslySetInnerHTML, dead, source, ...rest} = props
|
||||
const lang = getLanguage(props.className)
|
||||
if (lang && !dead) {
|
||||
const liveProps = {
|
||||
code: source,
|
||||
scope: {Octicon, getIconByName},
|
||||
transformCode: getTransformForLanguage(lang),
|
||||
mountStylesheet: false
|
||||
}
|
||||
return (
|
||||
<LiveProvider {...liveProps}>
|
||||
<BorderBox {...rest}>
|
||||
<BorderBox bg="white" p={3} border={0} borderBottom={1} borderRadius={0}>
|
||||
<Frame>
|
||||
<LivePreview />
|
||||
</Frame>
|
||||
</BorderBox>
|
||||
<Box is={Relative} bg="gray.1" p={3}>
|
||||
<LiveEditor style={{margin: 0, padding: 0}} />
|
||||
<Absolute right={0} top={0} m={3}>
|
||||
<ClipboardCopy value={source} />
|
||||
</Absolute>
|
||||
<Text
|
||||
as={LiveError}
|
||||
fontFamily="mono"
|
||||
style={{
|
||||
overflow: 'auto',
|
||||
whiteSpace: 'pre'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</BorderBox>
|
||||
</LiveProvider>
|
||||
)
|
||||
} else {
|
||||
const rest = {
|
||||
children,
|
||||
dangerouslySetInnerHTML
|
||||
}
|
||||
// eslint-disable-next-line react/no-danger-with-children
|
||||
return <BorderBox data-source={source} is="pre" {...rest} />
|
||||
}
|
||||
}
|
||||
|
||||
CodeExample.defaultProps = {
|
||||
my: 4
|
||||
}
|
||||
|
||||
function getLanguage(className) {
|
||||
const match = className && className.match(LANG_PATTERN)
|
||||
return match ? match[1] : undefined
|
||||
}
|
||||
|
||||
function getTransformForLanguage(lang) {
|
||||
return lang in languageTransforms ? languageTransforms[lang] : null
|
||||
}
|
9
docs/src/DetailsDialog.js
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
export default dynamic(
|
||||
() => {
|
||||
return import('details-dialog-element').then(() => React.createFactory('details-dialog'))
|
||||
},
|
||||
{ssr: false}
|
||||
)
|
65
docs/src/Frame.js
Normal file
@ -0,0 +1,65 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import Measure from 'react-measure'
|
||||
import {BorderBox} from '@primer/components'
|
||||
import {assetPrefix} from './utils'
|
||||
|
||||
const DEFAULT_IFRAME_HEIGHT = 150
|
||||
|
||||
export default class Frame extends React.Component {
|
||||
static defaultProps = {
|
||||
border: 0,
|
||||
borderRadius: 0,
|
||||
minHeight: 0,
|
||||
width: '100%'
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {files: [], height: props.height}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.doc = this.iframe.contentDocument
|
||||
const files = JSON.parse(document.body.dataset.files || '[]')
|
||||
// eslint-disable-next-line react/no-did-mount-set-state
|
||||
this.setState({loaded: false, files})
|
||||
this.iframe.addEventListener('load', () => {
|
||||
this.setState({loaded: true})
|
||||
})
|
||||
}
|
||||
|
||||
getHead() {
|
||||
const {files} = this.state
|
||||
return files
|
||||
? files
|
||||
.filter(file => file.endsWith('.css'))
|
||||
.map(file => <link rel="stylesheet" href={`${assetPrefix}/_next/${file}`} key={file} />)
|
||||
: null
|
||||
}
|
||||
|
||||
getBody(children) {
|
||||
return (
|
||||
<Measure bounds onResize={rect => this.setHeight(rect.bounds.height)}>
|
||||
{({measureRef}) => <div ref={measureRef}>{children}</div>}
|
||||
</Measure>
|
||||
)
|
||||
}
|
||||
|
||||
setHeight(height) {
|
||||
// console.warn('height:', height)
|
||||
this.setState({height})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {children, ...rest} = this.props
|
||||
const {height = 'auto'} = this.state
|
||||
return (
|
||||
<BorderBox as="iframe" style={{height}} {...rest} ref={node => (this.iframe = node)}>
|
||||
{this.doc
|
||||
? [ReactDOM.createPortal(this.getHead(), this.doc.head), ReactDOM.createPortal(this.getBody(children), this.doc.body)]
|
||||
: null}
|
||||
</BorderBox>
|
||||
)
|
||||
}
|
||||
}
|
58
docs/src/Header.js
Normal file
@ -0,0 +1,58 @@
|
||||
import React from 'react'
|
||||
import {withRouter} from 'next/router'
|
||||
import Octicon, {MarkGithub} from '@githubprimer/octicons-react'
|
||||
import {Text, Flex, Sticky, BorderBox, Box} from '@primer/components'
|
||||
import BoxShadow from './BoxShadow'
|
||||
import Link from './Link'
|
||||
import NodeLink from './NodeLink'
|
||||
|
||||
const NavLink = withRouter(({is: Tag = NodeLink, href, router, ...rest}) => (
|
||||
<Tag href={href} color="white" px={4} fontWeight={router.pathname === href ? 'bold' : null} {...rest} />
|
||||
))
|
||||
|
||||
const HeaderText = props => <Text fontSize={2} {...props} />
|
||||
|
||||
const Header = props => (
|
||||
<Sticky zIndex={100}>
|
||||
<BoxShadow py={3} bg="gray.9" color="white" {...props}>
|
||||
<Flex className="p-responsive" alignItems="center" justifyContent="space-between">
|
||||
<Link href="/css" color="white" ml={3}>
|
||||
<Flex alignItems="center" justifyContent="center">
|
||||
<Octicon icon={MarkGithub} size="medium" />
|
||||
<HeaderText ml={3}>Primer CSS</HeaderText>
|
||||
</Flex>
|
||||
</Link>
|
||||
<Box display={['none', 'none', 'block']}>
|
||||
<HeaderText>
|
||||
<NavLink href="/css">Docs</NavLink>
|
||||
<NavLink href="/css/getting-started" />
|
||||
<NavLink href="/css/principles" />
|
||||
<NavLink href="/css/tools" />
|
||||
<NavLink as={Link} href="https://github.com/primer/primer/releases">
|
||||
Releases
|
||||
</NavLink>
|
||||
</HeaderText>
|
||||
</Box>
|
||||
<Box display={['block', 'block', 'none']}>
|
||||
<Link href="#sidenav">
|
||||
<BorderBox
|
||||
border={1}
|
||||
borderColor="gray.6"
|
||||
borderRadius={3}
|
||||
color="white"
|
||||
display="inline-block"
|
||||
px="12px"
|
||||
py="6px"
|
||||
>
|
||||
<Text fontWeight="bold" fontSize={1}>
|
||||
Menu
|
||||
</Text>
|
||||
</BorderBox>
|
||||
</Link>
|
||||
</Box>
|
||||
</Flex>
|
||||
</BoxShadow>
|
||||
</Sticky>
|
||||
)
|
||||
|
||||
export default Header
|
11
docs/src/Link.js
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
import NextLink from 'next/link'
|
||||
import {Link as PrimerLink} from '@primer/components'
|
||||
|
||||
export default function Link({href, ...rest}) {
|
||||
return (
|
||||
<NextLink href={href}>
|
||||
<PrimerLink href={href} {...rest} />
|
||||
</NextLink>
|
||||
)
|
||||
}
|
33
docs/src/NodeLink.js
Normal file
@ -0,0 +1,33 @@
|
||||
import React from 'react'
|
||||
import Link from './Link'
|
||||
import {rootPage} from './utils'
|
||||
|
||||
/**
|
||||
* The NodeLink component takes an `href` and optional `children`.
|
||||
* If no `children` are provided, we look up the "node" of the corresponding
|
||||
* page in the tree (the one whose `path` matches the given `href`) and use
|
||||
* that node's `meta.title` if it's set. In other words, given the following
|
||||
* pages/foo/bar.md:
|
||||
*
|
||||
* ```md
|
||||
* ---
|
||||
* title: Foo Bar
|
||||
* ---
|
||||
* ```
|
||||
*
|
||||
* The following instance of NodeLink should render a link to "/foo/bar" with
|
||||
* "Foo Bar" as its text:
|
||||
*
|
||||
* ```jsx
|
||||
* <NodeLink href="/foo/bar" />
|
||||
* ```
|
||||
*/
|
||||
export default function NodeLink(props) {
|
||||
const {href, children: content} = props
|
||||
if (content) {
|
||||
return <Link {...props} />
|
||||
}
|
||||
const node = rootPage.first(node => node.path === href)
|
||||
const children = (node ? node.meta.title : null) || href
|
||||
return <Link {...props}>{children}</Link>
|
||||
}
|
30
docs/src/Outline.js
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react'
|
||||
import {Box} from '@primer/components'
|
||||
|
||||
export default function Outline({outline, ...rest}) {
|
||||
if (outline && outline.length) {
|
||||
return (
|
||||
<Box as="details" mb={4}>
|
||||
<summary>Table of contents</summary>
|
||||
<OutlineList items={outline} {...rest} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function OutlineList({items, ...rest}) {
|
||||
if (items && items.length) {
|
||||
return (
|
||||
<ul {...rest}>
|
||||
{items.map(item => (
|
||||
<li key={item.id}>
|
||||
<a href={`#${item.id}`}>{item.title}</a>
|
||||
<OutlineList items={item.children} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
67
docs/src/PackageHeader.js
Normal file
@ -0,0 +1,67 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {Comment, Info, FileCode, Alert} from '@githubprimer/octicons-react'
|
||||
import {Box, Flex, Link, StyledOcticon as Octicon, Text} from '@primer/components'
|
||||
import StatusLabel from './StatusLabel'
|
||||
|
||||
export default function PackageHeader(props) {
|
||||
const {description, internal = false, package: pkg, source = '', status, status_issue: issue, ...rest} = props
|
||||
|
||||
const isPackage = pkg && source.indexOf('/modules/') > -1
|
||||
const isInternal = internal || source.indexOf('https://github.com/github/github') === 0
|
||||
|
||||
let info
|
||||
if (isInternal) {
|
||||
info = (
|
||||
<Text fontWeight="bold" color="orange.5">
|
||||
<Octicon icon={Alert} mr={1} />
|
||||
For GitHub internal use only
|
||||
</Text>
|
||||
)
|
||||
} else if (isPackage) {
|
||||
info = (
|
||||
<>
|
||||
<Text fontWeight="bold" mr={2}>
|
||||
Package:
|
||||
</Text>
|
||||
<Link href={`https://npm.im/${pkg.name}`}>{pkg.name}</Link>
|
||||
</>
|
||||
)
|
||||
} else if (description) {
|
||||
info = (
|
||||
<Text color="gray.5">
|
||||
<Octicon icon={Info} mr={2} />
|
||||
{description}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex justifyContent="space-between" mb={4} {...rest}>
|
||||
<Text as="div" fontSize={1}>
|
||||
{status ? <StatusLabel status={status} as="a" href="/css/status-key" mr={2} /> : null}
|
||||
{info}
|
||||
</Text>
|
||||
<Box>
|
||||
{issue ? (
|
||||
<Link href={issue} ml={2}>
|
||||
<Octicon icon={Comment} />
|
||||
</Link>
|
||||
) : null}
|
||||
{source ? (
|
||||
<Link href={source} ml={2}>
|
||||
<Octicon icon={FileCode} />
|
||||
</Link>
|
||||
) : null}
|
||||
</Box>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
PackageHeader.propTypes = {
|
||||
package: PropTypes.shape({
|
||||
name: PropTypes.string.isRequired
|
||||
}),
|
||||
source: PropTypes.string,
|
||||
status: PropTypes.string
|
||||
}
|
154
docs/src/SideNav.js
Normal file
@ -0,0 +1,154 @@
|
||||
import React from 'react'
|
||||
import {join} from 'path'
|
||||
import {withRouter} from 'next/router'
|
||||
import {Box, BorderBox, Flex, Relative} from '@primer/components'
|
||||
import NodeLink from './NodeLink'
|
||||
import {rootPage} from './utils'
|
||||
|
||||
export default function SideNav(props) {
|
||||
return (
|
||||
<Relative as="nav">
|
||||
<Box id="sidenav" {...props}>
|
||||
<Flex flexDirection="column" alignItems="start">
|
||||
<Router>
|
||||
<Section path="/css/getting-started" />
|
||||
<Section path="/css/principles" />
|
||||
<Section path="/css/tools" />
|
||||
<Section path="/css/whats-new" />
|
||||
<RouteMatch path="/css">
|
||||
<Section>
|
||||
<SectionLink href="status-key" />
|
||||
</Section>
|
||||
<Section path="support" />
|
||||
<Section path="utilities" />
|
||||
<Section path="objects" />
|
||||
<Section path="components" />
|
||||
</RouteMatch>
|
||||
</Router>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Relative>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A <Section> gets a `path` and optional children. If it has children it will
|
||||
* render those and prepend each child's `href` prop with the provided `path`.
|
||||
* This means that you can do:
|
||||
*
|
||||
* ```jsx
|
||||
* <Section path="/section">
|
||||
* <Link href="foo">Links to /section/foo</Link>
|
||||
* </Section>
|
||||
* ```
|
||||
*
|
||||
* If no children are provided, it renders a <NavList> with the provided
|
||||
* `path`.
|
||||
*/
|
||||
const Section = ({path, children}) => (
|
||||
<BorderBox p={5} border={0} borderBottom={1} borderRadius={0} width="100%">
|
||||
{children && path ? React.Children.map(children, child => addPath(child, path)) : <NavList path={path} />}
|
||||
</BorderBox>
|
||||
)
|
||||
|
||||
/**
|
||||
* A <NavList> renders a <SectionLink> for the given `path` and looks up the
|
||||
* path in the page tree. If a node is found, it renders a <NavLink> for each
|
||||
* of the node's children.
|
||||
*/
|
||||
function NavList({path}) {
|
||||
const node = rootPage.first(node => node.path === path)
|
||||
const children = node ? node.children.sort(nodeSort) : []
|
||||
return (
|
||||
<>
|
||||
<SectionLink href={path} mb={3} />
|
||||
{children.map(child => (
|
||||
<NavLink href={child.path} key={child.path} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A <SectionLink> is really just a <NodeLink> that's bold when its `href`
|
||||
* matches the current path, wrapped in a <Box> for whitespace.
|
||||
*/
|
||||
const SectionLink = withRouter(({href, router, ...rest}) => (
|
||||
<Box {...rest}>
|
||||
<NodeLink href={href} color="gray.9" fontSize={2} fontWeight={router.pathname.startsWith(href) ? 'bold' : null} />
|
||||
</Box>
|
||||
))
|
||||
|
||||
/**
|
||||
* A <NavLink> is a <NodeLink> that turns black when its `href` matches the
|
||||
* current path, wrapped in a <Box> for whitespace.
|
||||
*/
|
||||
const NavLink = withRouter(({href, router, ...rest}) => {
|
||||
return (
|
||||
<Box mt={2}>
|
||||
<NodeLink href={href} color={router.pathname === href ? 'black' : undefined} fontSize={1} {...rest} />
|
||||
</Box>
|
||||
)
|
||||
})
|
||||
|
||||
/**
|
||||
* This inspired React Router's <Router> component, in that it looks for
|
||||
* children with the `path` prop and only renders the _first_ one that matches
|
||||
* the beginning of the current path. Children without a `path` prop are always
|
||||
* rendered.
|
||||
*/
|
||||
const Router = withRouter(({router, children}) => {
|
||||
let matched = false
|
||||
return React.Children.toArray(children).map(child => {
|
||||
if (child.props.path) {
|
||||
if (!matched && router.pathname.indexOf(child.props.path) === 0) {
|
||||
return (matched = child)
|
||||
}
|
||||
} else {
|
||||
return child
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* <RouteMatch> is just a way to conditonally render content without a wrapper
|
||||
* element when contained directly in a <Router>:
|
||||
*
|
||||
* ```jsx
|
||||
* <Router>
|
||||
* <RouteMatch path="/some/dir">
|
||||
* this will only show up on pages whose path begins with "/some/dir"
|
||||
* </RouteMatch>
|
||||
* </Router>
|
||||
* ```
|
||||
*/
|
||||
function RouteMatch({path, children}) {
|
||||
return path ? React.Children.map(children, child => addPath(child, path)) : children
|
||||
}
|
||||
|
||||
function sortCompare(a, b, get) {
|
||||
const aa = get(a)
|
||||
const bb = get(b)
|
||||
return typeof aa === 'string' && typeof bb === 'string' ? aa.localeCompare(bb) : undefined
|
||||
}
|
||||
|
||||
function nodeSort(a, b) {
|
||||
return sortCompare(a, b, node => node.meta.sort_title || node.meta.title)
|
||||
}
|
||||
|
||||
function addPath(el, path) {
|
||||
// if there's no path, just return the element
|
||||
if (!path) return el
|
||||
|
||||
// if this is a link it'll have an "href"; otherwise, add "path"
|
||||
const prop = el.props.href ? 'href' : 'path'
|
||||
const value = el.props[prop]
|
||||
const props = {}
|
||||
// if there's a value and it's not absolute, prefix it with the path
|
||||
if (value && !value.match(/^(\/|https?:)/)) {
|
||||
props[prop] = join(path, value)
|
||||
} else {
|
||||
props[prop] = path
|
||||
}
|
||||
return React.cloneElement(el, props)
|
||||
}
|
32
docs/src/StatusLabel.js
Normal file
@ -0,0 +1,32 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {BorderBox, StyledOcticon as Octicon, Text} from '@primer/components'
|
||||
import {PrimitiveDot} from '@githubprimer/octicons-react'
|
||||
|
||||
const StyledLabel = styled(BorderBox)`
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
`
|
||||
|
||||
export const STATUS_COLORS = {
|
||||
stable: 'green.6',
|
||||
'new release': 'green.6',
|
||||
experimental: 'yellow.7',
|
||||
'in review': 'yellow.7',
|
||||
deprecated: 'red.6'
|
||||
}
|
||||
|
||||
export default function StatusLabel({status, children, ...rest}) {
|
||||
return (
|
||||
<StyledLabel px={2} py={1} {...rest}>
|
||||
<Octicon icon={PrimitiveDot} color={getStatusColor(status)} mr={2} />
|
||||
<Text color="black" fontSize={1}>
|
||||
{children || status}
|
||||
</Text>
|
||||
</StyledLabel>
|
||||
)
|
||||
}
|
||||
|
||||
export function getStatusColor(status) {
|
||||
return STATUS_COLORS[status.toLowerCase()] || 'gray.5'
|
||||
}
|
184
docs/src/color-system.js
Normal file
@ -0,0 +1,184 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import chroma from 'chroma-js'
|
||||
import colors from 'primer-colors'
|
||||
import titleCase from 'title-case'
|
||||
import {BorderBox, Box, Flex, Heading, Text} from '@primer/components'
|
||||
|
||||
const gradientHues = ['gray', 'blue', 'green', 'purple', 'yellow', 'orange', 'red']
|
||||
|
||||
const {black: BLACK, white: WHITE} = colors
|
||||
|
||||
export function ColorPalette(props) {
|
||||
return (
|
||||
<Flex mb={6} className="markdown-no-margin" {...props}>
|
||||
{gradientHues.map(hue => {
|
||||
const color = colors[hue][5]
|
||||
return (
|
||||
<Box bg={color} p={3} width={200} mr={2} key={hue}>
|
||||
<Text fontWeight="bold" color={overlayColor(color)}>
|
||||
{titleCase(hue)}
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
})}
|
||||
<BorderBox bg="white" p={3} width={200} borderRadius={0}>
|
||||
<Text fontWeight="bold" color="black">
|
||||
White
|
||||
</Text>
|
||||
</BorderBox>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
export function ColorVariables(props) {
|
||||
return (
|
||||
<>
|
||||
<Flex flexWrap="wrap" className="gutter" {...props}>
|
||||
{gradientHues.map(hue => (
|
||||
<ColorVariable id={hue} hue={hue} key={hue} />
|
||||
))}
|
||||
</Flex>
|
||||
<Flex flexWrap="wrap" {...props}>
|
||||
<FadeVariables id="black" hue="black" bg="black" color="white">
|
||||
<BorderBox border={0} borderRadius={0} borderTop={1} borderColor="gray.5" mt={1}>
|
||||
<Text as="div" fontSize={2} pt={3} mb={0}>
|
||||
Black fades apply alpha transparency to the <Var>$black</Var> variable. The black color value has a slight
|
||||
blue hue to match our grays.
|
||||
</Text>
|
||||
</BorderBox>
|
||||
</FadeVariables>
|
||||
<FadeVariables id="white" hue="white" over={BLACK}>
|
||||
<BorderBox border={0} borderRadius={0} borderTop={1} mt={1}>
|
||||
<Text as="div" fontSize={2} pt={3} mb={0}>
|
||||
White fades apply alpha transparency to the <Var>$white</Var> variable, below these are shown overlaid on
|
||||
a dark gray background.
|
||||
</Text>
|
||||
</BorderBox>
|
||||
</FadeVariables>
|
||||
</Flex>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function ColorVariable({hue, ...rest}) {
|
||||
const values = colors[hue]
|
||||
return (
|
||||
<Flex.Item as={Box} pr={4} mb={6} className="col-6 markdown-no-margin" {...rest}>
|
||||
{/* <Heading as="div">{titleCase(hue)}</Heading> */}
|
||||
<Box bg={`${hue}.5`} my={2} p={3} color="white">
|
||||
<Heading as="div" pb={3} fontSize={56} fontWeight="light">
|
||||
{titleCase(hue)}
|
||||
</Heading>
|
||||
<Flex justifyContent="space-between">
|
||||
<Flex.Item flex="1 1 auto" as={Var}>
|
||||
${hue}-500
|
||||
</Flex.Item>
|
||||
<Text fontFamily="mono">{values[5]}</Text>
|
||||
</Flex>
|
||||
</Box>
|
||||
{values.map((value, i) => (
|
||||
<Swatch name={`${hue}-${i}00`} value={value} key={value} />
|
||||
))}
|
||||
</Flex.Item>
|
||||
)
|
||||
}
|
||||
|
||||
ColorVariable.propTypes = {
|
||||
hue: PropTypes.oneOf(Object.keys(colors)).isRequired
|
||||
}
|
||||
|
||||
export function FadeVariables({hue, color, bg, over, children, ...rest}) {
|
||||
const colorValue = colors[hue]
|
||||
const alphas = [15, 30, 50, 70, 85]
|
||||
const values = alphas.map(alpha => {
|
||||
const value = chroma(colorValue)
|
||||
.alpha(alpha / 100)
|
||||
.css()
|
||||
return {
|
||||
name: `${hue}-fade-${alpha}`,
|
||||
textColor: fadeTextColor(value, over),
|
||||
value
|
||||
}
|
||||
})
|
||||
const boxProps = {color, bg}
|
||||
return (
|
||||
<Flex.Item as={Box} pr={4} mb={6} width={1 / 2} className="markdown-no-margin" {...rest}>
|
||||
{/* <Heading as="div">{titleCase(hue)}</Heading> */}
|
||||
<Box my={2} p={3} {...boxProps}>
|
||||
<Heading as="div" pb={3} fontSize={56} fontWeight="light">
|
||||
{titleCase(hue)}
|
||||
</Heading>
|
||||
<Flex justifyContent="space-between">
|
||||
<Flex.Item flex="1 1 auto" as={Var}>
|
||||
${hue}
|
||||
</Flex.Item>
|
||||
<Text fontFamily="mono">
|
||||
{chroma(colorValue).css()}
|
||||
{' / '}
|
||||
{colorValue}
|
||||
</Text>
|
||||
</Flex>
|
||||
{children}
|
||||
</Box>
|
||||
<Box bg={over}>
|
||||
{values.map(swatchProps => (
|
||||
<Swatch {...swatchProps} key={swatchProps.name} />
|
||||
))}
|
||||
</Box>
|
||||
</Flex.Item>
|
||||
)
|
||||
}
|
||||
|
||||
FadeVariables.propTypes = {
|
||||
bg: Box.propTypes.color,
|
||||
color: Box.propTypes.color,
|
||||
hue: PropTypes.oneOf(['black', 'white']),
|
||||
over: PropTypes.string
|
||||
}
|
||||
|
||||
function Swatch(props) {
|
||||
const {name, value, textColor = overlayColor(value), ...rest} = props
|
||||
return (
|
||||
<Box bg={value} color={textColor} {...rest}>
|
||||
<Text as={Flex} fontSize={1} justifyContent="space-between">
|
||||
<Box p={3}>
|
||||
<Var>${name}</Var>
|
||||
</Box>
|
||||
<Box p={3}>
|
||||
<Text fontFamily="mono">{value}</Text>
|
||||
</Box>
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
Swatch.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
textColor: PropTypes.string,
|
||||
value: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
function Var(props) {
|
||||
// FIXME: fontStyle should be a prop, right?
|
||||
return <Text as="var" fontWeight="bold" fontFamily="mono" style={{fontStyle: 'normal'}} {...props} />
|
||||
}
|
||||
|
||||
function overlayColor(bg) {
|
||||
return chroma(bg).luminance() > 0.5 ? BLACK : WHITE
|
||||
}
|
||||
|
||||
function fadeTextColor(fg, bg = WHITE) {
|
||||
const rgb = compositeRGB(fg, bg)
|
||||
return overlayColor(rgb)
|
||||
}
|
||||
|
||||
/**
|
||||
* Composite ("flatten") a foreground RGBA value with an RGB background into an
|
||||
* RGB color for the purposes of measuring contrast or luminance.
|
||||
*/
|
||||
function compositeRGB(foreground, background) {
|
||||
const [fr, fg, fb, fa] = chroma(foreground).rgba()
|
||||
const [br, bg, bb] = chroma(background).rgb()
|
||||
return chroma([(1 - fa) * br + fa * fr, (1 - fa) * bg + fa * fg, (1 - fa) * bb + fa * fb]).css()
|
||||
}
|
8
docs/src/components.js
Normal file
@ -0,0 +1,8 @@
|
||||
export {default as BoxShadow} from './BoxShadow'
|
||||
// export {default as ClipboardCopy} from './ClipboardCopy'
|
||||
export {default as CodeExample} from './CodeExample'
|
||||
export {default as Header} from './Header'
|
||||
export {default as Link} from './Link'
|
||||
export {default as NodeLink} from './NodeLink'
|
||||
export {default as PackageHeader} from './PackageHeader'
|
||||
export {default as SideNav} from './SideNav'
|
5
docs/src/constants.js
Normal file
@ -0,0 +1,5 @@
|
||||
export const WCAG_AA = 4.5
|
||||
export const WCAG_AAA = 7
|
||||
export const MIN_CONTRAST_RATIO = WCAG_AAA
|
||||
|
||||
export const CONTENT_MAX_WIDTH = 1012
|
2
docs/src/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './components'
|
||||
export * from './utils'
|
29
docs/src/landing/ColorImage.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<svg width="100" height="108" viewBox="0 0 108 108" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Group</title>
|
||||
<desc>Created using Figma</desc>
|
||||
<g id="Canvas" transform="translate(-12627 -7962)">
|
||||
<g id="Group">
|
||||
<g id="Ellipse 3">
|
||||
<use xlink:href="#path0_fill" transform="translate(12647.8 8003.54)" fill="#2088FF" fill-opacity="0.3"></use>
|
||||
</g>
|
||||
<g id="Ellipse 3">
|
||||
<use xlink:href="#path0_fill" transform="translate(12668.5 7970.31)" fill="#2088FF" fill-opacity="0.3"></use>
|
||||
</g>
|
||||
<g id="Ellipse 3">
|
||||
<use xlink:href="#path0_fill" transform="translate(12627 7970.31)" fill="#2088FF" fill-opacity="0.3"></use>
|
||||
</g>
|
||||
<g id="Ellipse 3">
|
||||
<use xlink:href="#path0_fill" transform="translate(12627 7995.23)" fill="#2088FF" fill-opacity="0.3"></use>
|
||||
</g>
|
||||
<g id="Ellipse 3">
|
||||
<use xlink:href="#path0_fill" transform="translate(12668.5 7995.23)" fill="#2088FF" fill-opacity="0.3"></use>
|
||||
</g>
|
||||
<g id="Ellipse 3">
|
||||
<use xlink:href="#path0_fill" transform="translate(12647.8 7962)" fill="#2088FF" fill-opacity="0.3"></use>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<path id="path0_fill" d="M 66.4615 33.2308C 66.4615 51.5836 51.5836 66.4615 33.2308 66.4615C 14.8779 66.4615 0 51.5836 0 33.2308C 0 14.8779 14.8779 0 33.2308 0C 51.5836 0 66.4615 14.8779 66.4615 33.2308Z"></path>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
docs/src/landing/ComponentsImage.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="0 0 73.15 55.6" class="mb-3" width="72"><title>styles</title><path d="M70.1 55.6H42.55a3 3 0 0 1-3-3v-9.46a1 1 0 0 1 2 0v9.46a1 1 0 0 0 1 1h27.6a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-27.6a1 1 0 0 0-1 1v10.8a1 1 0 1 1-2 0V3a3 3 0 0 1 3-3h27.6a3 3 0 0 1 3 3v49.6a3 3 0 0 1-3 3h-.05zM30.55 47.8H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h27.55a3 3 0 0 1 3 3v10.8a1 1 0 0 1-2 0V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v41.8a1 1 0 0 0 1 1h27.55a1 1 0 0 0 1-1v-1.62a1 1 0 0 1 2 0v1.62a3 3 0 0 1-3 3z" fill="#79b8ff"></path><path fill="#79b8ff" d="M40.5 8.6h31v2h-31zM1.6 8.6h31v2h-31z"></path><path d="M62.3 38.15H21.9a3 3 0 0 1-3-3v-13.9a3 3 0 0 1 3-3h40.4a3 3 0 0 1 3 3v13.9a3 3 0 0 1-3 3zm-40.4-17.9a1 1 0 0 0-1 1v13.9a1 1 0 0 0 1 1h40.4a1 1 0 0 0 1-1v-13.9a1 1 0 0 0-1-1H21.9z" fill="#2088ff"></path><path d="M31.3 32.25h-3.64a2.39 2.39 0 0 1-2.11-2.3v-3.5a2.33 2.33 0 0 1 2.3-2.3h3.5a2.33 2.33 0 0 1 2.2 2.3v3.5a2.33 2.33 0 0 1-2.25 2.3zm-3.41-2h3.41a.32.32 0 0 0 .3-.3v-3.5a.32.32 0 0 0-.3-.3h-3.5a.32.32 0 0 0-.3.3v3.5a.43.43 0 0 0 .39.3zM57.1 29.15H38.7a1 1 0 0 1 0-2h18.4a1 1 0 0 1 0 2zM15.92 29.22a1 1 0 0 1-.45-.11l-.47-.2a6.86 6.86 0 0 1-.66-.26 1 1 0 0 1 .89-1.79l.47.2a6.86 6.86 0 0 1 .66.28 1 1 0 0 1-.44 1.88zM11.95 26.76a1 1 0 0 1-.71-.29 12.44 12.44 0 0 1-1.69-2.06 1 1 0 0 1 1.68-1.08A10.39 10.39 0 0 0 12.65 25a1 1 0 0 1-.7 1.76zM9.29 21.83a1 1 0 0 1-1-.76 6.13 6.13 0 0 1-.12-1.47 1 1 0 0 1 2 0 4.78 4.78 0 0 0 .06 1 1 1 0 0 1-.74 1.2z" fill="#2088ff"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
1418
docs/src/landing/HeaderImage.svg
Normal file
After Width: | Height: | Size: 94 KiB |
1
docs/src/landing/ObjectsImage.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="0 0 53.46 55.1" class="mb-3" width="60"><title>objects</title><path d="M38.1 41.9L2 41.8a2.08 2.08 0 0 1-2-2L.1 2a2 2 0 0 1 2-2l36.1.1a2 2 0 0 1 2 2l-.1 37.8a2 2 0 0 1-2 2zM2 39.75l36.1.16.1-37.8L2.1 2z" fill="#2088ff"></path><path d="M44.9 48.5L8 48.4a1 1 0 1 1 0-2l36.8.1.1-38.4a1 1 0 0 1 1-1 1 1 0 0 1 1 1l-.1 38.5a1.9 1.9 0 0 1-1.9 1.9z" fill="#79b8ff"></path><path d="M51.5 55.1L14.56 55a1 1 0 1 1 0-2l36.8.1.1-38.4a1 1 0 0 1 1-1 1 1 0 0 1 1 1l-.06 38.49a1.9 1.9 0 0 1-1.89 1.91z" fill="#79b8ff"></path><path d="M29.7 32.6H10.5a1 1 0 1 1 0-2h19.2a1 1 0 0 1 0 2zM29.7 25.5H10.5a1 1 0 1 1 0-2h19.2a1 1 0 0 1 0 2zM29.7 18.4H10.5a1 1 0 1 1 0-2h19.2a1 1 0 1 1 0 2zM20.1 11.3h-9.6a1 1 0 1 1 0-2h9.6a1 1 0 1 1 0 2z" fill="#2088ff"></path></svg>
|
After Width: | Height: | Size: 812 B |
27
docs/src/landing/SpacingImage.svg
Normal file
@ -0,0 +1,27 @@
|
||||
<svg width="100" height="100" viewBox="0 0 120 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Group</title>
|
||||
<desc>Created using Figma</desc>
|
||||
<g id="Canvas" transform="translate(-12622 -7539)">
|
||||
<g id="Group">
|
||||
<g id="Line 5">
|
||||
<use xlink:href="#path0_stroke" transform="matrix(6.13546e-17 1 -1 6.11103e-17 12740 7539)" fill="#2088FF"></use>
|
||||
</g>
|
||||
<g id="Line 5">
|
||||
<use xlink:href="#path0_stroke" transform="matrix(6.12323e-17 1 -1 6.12323e-17 12622 7539)" fill="#2088FF"></use>
|
||||
</g>
|
||||
<g id="Line 5">
|
||||
<use xlink:href="#path0_stroke" transform="matrix(6.16285e-17 1 -1 6.08387e-17 12681 7539)" fill="#79B8FF"></use>
|
||||
</g>
|
||||
<g id="Rectangle 10">
|
||||
<use xlink:href="#path1_fill" transform="translate(12640.2 7559.69)" fill="#79B8FF"></use>
|
||||
</g>
|
||||
<g id="Rectangle 10">
|
||||
<use xlink:href="#path1_fill" transform="translate(12699.2 7559.69)" fill="#79B8FF"></use>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<path id="path0_stroke" d="M 0 0L 100 0L 100 -2L 0 -2L 0 0Z"></path>
|
||||
<path id="path1_fill" d="M 0 0L 22.5588 0L 22.5588 58.6207L 0 58.6207L 0 0Z"></path>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
1
docs/src/landing/UtilitiesImage.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="0 0 58.5 58.7" class="mb-3" width="62"><title>utilities</title><path d="M41.6 15.6l-1.4-1.4a1 1 0 1 0-1.4 1.4l1.4 1.4a1 1 0 0 0 1.4 0 1 1 0 0 0 0-1.4zM33.9 7.9a1 1 0 1 0-1.4 1.4l1.4 1.4a1 1 0 0 0 1.4 0 1 1 0 0 0 0-1.4zM27.9 26.6a1 1 0 0 0-1.4 1.4l1.4 1.4a1 1 0 0 0 1.4 0 1 1 0 0 0 0-1.4zM21.6 20.2a1 1 0 0 0-1.4 1.4l1.4 1.4a1 1 0 0 0 1.4 0 1 1 0 0 0 0-1.4zM15.4 38.7a1 1 0 0 0-1.4 1.4l1.4 1.4a1 1 0 0 0 1.4 0 1 1 0 0 0 0-1.4zM9.3 32.7a1 1 0 1 0-1.4 1.4l1.4 1.4a1 1 0 0 0 1.4 0 1 1 0 0 0 0-1.4z" fill="#6bbcff"></path><path d="M26.6 13.4V1a.94.94 0 0 0-1-1H1a.94.94 0 0 0-1 1v24.8a.94.94 0 0 0 1 1h12.4a.94.94 0 0 0 1-1V14.4h11.2a1.08 1.08 0 0 0 1-1zm-2-1H13.4a.94.94 0 0 0-1 1v11.4H2V2h22.6v10.4zM58.2 19.9a.91.91 0 0 0-.7-.3H45.1a.94.94 0 0 0-1 1v11.3H33.9a1.9 1.9 0 0 0-1.9 1.9v10.6H21.5a1.9 1.9 0 0 0-1.9 1.9v10.5a1.9 1.9 0 0 0 1.9 1.9h36a.94.94 0 0 0 1-1V20.6a.91.91 0 0 0-.3-.7zm-1.7 1.7v22.8H46.1V21.7zM44.1 34v21.1L33.9 44.9 33.8 34h10.3zM21.5 46.4h11l10.4 10.4H21.5V46.4zm24.6 10.3V46.4h10.4v10.4z" fill="#008fff"></path></svg>
|
After Width: | Height: | Size: 1.1 KiB |
175
docs/src/landing/index.js
Normal file
@ -0,0 +1,175 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {BorderBox, Box, Flex, Heading, Link, Text} from '@primer/components'
|
||||
import {getAssetPath} from '../utils'
|
||||
import UtilitiesImage from './UtilitiesImage.svg'
|
||||
import ObjectsImage from './ObjectsImage.svg'
|
||||
import ComponentsImage from './ComponentsImage.svg'
|
||||
import SpacingImage from './SpacingImage.svg'
|
||||
import ColorImage from './ColorImage.svg'
|
||||
|
||||
export {default as HeaderImage} from './HeaderImage.svg'
|
||||
|
||||
const OverviewTitle = props => <Heading fontSize={3} fontWeight="normal" as="div" {...props} />
|
||||
const OverviewText = props => <Text fontSize={1} {...props} />
|
||||
|
||||
export function StylesOverview(props) {
|
||||
const styleTypes = [
|
||||
{
|
||||
name: 'Utilities',
|
||||
desc: 'Single purpose, immutable styles, that do one thing well.',
|
||||
image: UtilitiesImage
|
||||
},
|
||||
{
|
||||
name: 'Objects',
|
||||
desc: 'Scaffolding for common page and content layouts.',
|
||||
image: ObjectsImage
|
||||
},
|
||||
{
|
||||
name: 'Components',
|
||||
desc: 'Abstracted patterns for frequently used visual styles.',
|
||||
image: ComponentsImage
|
||||
}
|
||||
]
|
||||
return (
|
||||
<Flex {...props}>
|
||||
{styleTypes.map(({name, desc, image}) => (
|
||||
<Flex.Item as={Text} textAlign="center" mx={4} key={name}>
|
||||
<Image src={image} height={90} mb={2} />
|
||||
<OverviewTitle>{name}</OverviewTitle>
|
||||
<OverviewText>{desc}</OverviewText>
|
||||
</Flex.Item>
|
||||
))}
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
StylesOverview.propTypes = {
|
||||
types: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
name: PropTypes.node,
|
||||
desc: PropTypes.node,
|
||||
image: PropTypes.func
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export function PrimitivesOverview(props) {
|
||||
const primitiveTypes = [
|
||||
{
|
||||
name: 'Highly composable spacing scale',
|
||||
desc: `The base-8 spacing scale is highly composable and works with the density of GitHub’s content. Margin and padding spacers bring consistency to vertical and horizontal rhythm, while remaining flexible so you can tweak layouts to work for every context.`,
|
||||
image: SpacingImage
|
||||
},
|
||||
{
|
||||
name: 'Customizable typography',
|
||||
desc: `Font size and line-height options work together to result in more sensible numbers. Font styles come in a range of weights and sizes so that we can style appropriately for content and readability. Type utilities allow us to change the visual styles while keeping markup semantic.`,
|
||||
image: getAssetPath('typography.png')
|
||||
},
|
||||
{
|
||||
name: 'Meaningful color',
|
||||
desc: `The color system allows us to add meaningful signals to content and interactions. Color variables and utilities offer thematic styling options without being tied to structure. Text and background colors come in a range of accessible combinations to ensure we build inclusive interfaces.`,
|
||||
image: ColorImage
|
||||
}
|
||||
]
|
||||
return (
|
||||
<Box width={['auto', 'auto', 10 / 12]} mx="auto" {...props}>
|
||||
{primitiveTypes.map(({name, desc, image}) => (
|
||||
<Flex key={name} my={6} alignItems="center">
|
||||
<Box width={300} mr={6} py={2}>
|
||||
<Image src={image} />
|
||||
</Box>
|
||||
<Box>
|
||||
<OverviewTitle>{name}</OverviewTitle>
|
||||
<OverviewText>{desc}</OverviewText>
|
||||
</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
PrimitivesOverview.propTypes = {
|
||||
types: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
name: PropTypes.node,
|
||||
desc: PropTypes.node,
|
||||
image: PropTypes.func
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function Image(props) {
|
||||
const {src, ...rest} = props
|
||||
switch (typeof src) {
|
||||
case 'string':
|
||||
return <Box as="img" width="100%" alt="" {...props} />
|
||||
case 'function':
|
||||
return <Box as={src} {...rest} />
|
||||
default:
|
||||
throw new Error(`Unrecognized Image.src type: "${typeof src}"`)
|
||||
}
|
||||
}
|
||||
|
||||
export function PrimerPackageBox({data = {}, count, ...rest}) {
|
||||
return (
|
||||
<Flex justifyContent="space-around" {...rest}>
|
||||
<BorderBox bg="gray.1" width="auto" px={6} py={3} my={4}>
|
||||
<Flex alignItems="center" justifyContent="space-around">
|
||||
<Text fontSize={3} fontWeight="bold" mb={2} textAlign="center">
|
||||
<Link href={packageSourceURL('primer')} color="inherit">
|
||||
Primer
|
||||
</Link>{' '}
|
||||
<Link href={packageURL('primer')}>{data.version}</Link>
|
||||
</Text>
|
||||
<Link href="https://travis-ci.org/primer/primer" mt={-1}>
|
||||
<img alt="Build Status" src="https://travis-ci.org/primer/primer.svg?branch=master" />
|
||||
</Link>
|
||||
</Flex>
|
||||
{count ? (
|
||||
<Text as="div" textAlign="center">
|
||||
This package includes all {count} Primer modules.
|
||||
</Text>
|
||||
) : null}
|
||||
</BorderBox>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
export function MetaPackageBox({children, data = {}, title, ...rest}) {
|
||||
const deps = data.dependencies || []
|
||||
return (
|
||||
<Flex.Item as={BorderBox} bg="white" maxWidth={220} {...rest}>
|
||||
<BorderBox bg="gray.1" border={0} borderBottom={1} borderRadius={0} px={3} py={2}>
|
||||
<Heading as="div" fontSize={2}>
|
||||
<Link href={packageSourceURL(data.name)} color="inherit">
|
||||
{title}
|
||||
</Link>{' '}
|
||||
<Link href={packageURL(data.name)}>{data.version}</Link>
|
||||
</Heading>
|
||||
</BorderBox>
|
||||
<Text is="div" fontSize={1} p={3}>
|
||||
{children}
|
||||
<Text is="div" fontWeight="bold" mt={4} mb={2}>
|
||||
{deps.length} packages:
|
||||
</Text>
|
||||
<ul className="list-style-none pl-0">
|
||||
{deps.map(dep => (
|
||||
<li key={dep}>
|
||||
<Link href={packageURL(dep)}>{dep}</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Text>
|
||||
</Flex.Item>
|
||||
)
|
||||
}
|
||||
|
||||
function packageURL(name) {
|
||||
return `https://www.npmjs.com/package/${name}`
|
||||
}
|
||||
|
||||
function packageSourceURL(name, branch = 'master') {
|
||||
// TODO get this from Metalsmith or page metadata???
|
||||
return `https://github.com/primer/primer/blob/${branch}/modules/${name}`
|
||||
}
|
28
docs/src/markdown.js
Normal file
@ -0,0 +1,28 @@
|
||||
import React from 'react'
|
||||
import {Heading, Link} from '@primer/components'
|
||||
import CodeExample from './CodeExample'
|
||||
import Outline from './Outline'
|
||||
|
||||
export const H1 = props => <Heading fontSize={6} fontWeight="light" {...props} />
|
||||
|
||||
export default function getComponents(page = {}) {
|
||||
const {outline: getOutline = () => []} = page
|
||||
|
||||
return {
|
||||
h1: H1,
|
||||
// render links with our component
|
||||
a: Link,
|
||||
// render code blocks with our wrapper around mdx-live
|
||||
code: CodeExample,
|
||||
// render the outline for <p> tags with exactly the text "{:toc}"
|
||||
p: ({children, ...rest}) => {
|
||||
if (children === '{:toc}') {
|
||||
return <Outline outline={getOutline()} {...rest} />
|
||||
} else {
|
||||
return <p {...rest}>{children}</p>
|
||||
}
|
||||
},
|
||||
// "unwrap" <pre> elements around <code> blocks
|
||||
pre: props => props.children
|
||||
}
|
||||
}
|
30
docs/src/redirect.js
Normal file
@ -0,0 +1,30 @@
|
||||
import Router from 'next/router'
|
||||
|
||||
/**
|
||||
* Export this as your default from a page, and it'll redirect both server-
|
||||
* and client-side:
|
||||
*
|
||||
* ```js
|
||||
* import {redirect} from '../src/utils'
|
||||
* export default redirect('/some/path')
|
||||
* ```
|
||||
*/
|
||||
export default function redirect(uri, status = 303) {
|
||||
// XXX this doesn't need to extend React.Component because
|
||||
// it doesn't "do" anything React-y
|
||||
return class {
|
||||
static getInitialProps({res}) {
|
||||
// the "context" object passed to getInitialProps() will
|
||||
// have a "res" (response) object if we're server-side
|
||||
if (res) {
|
||||
res.writeHead(status, {Location: uri})
|
||||
res.end()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
Router.replace(uri)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
87
docs/src/utils.js
Normal file
@ -0,0 +1,87 @@
|
||||
import React from 'react'
|
||||
import getConfig from 'next/config'
|
||||
import TreeModel from 'tree-model'
|
||||
|
||||
export const CommonStyles = () => {
|
||||
const sheets = [getAssetPath('github/styleguide.css')]
|
||||
return sheets.map(href => <link href={href} rel="stylesheet" key={href} />)
|
||||
}
|
||||
|
||||
export const CommonScripts = () => <script src={getAssetPath('github/styleguide.js')} />
|
||||
|
||||
const INDEX_PATTERN = /\/index(\.[a-z]+)?$/
|
||||
|
||||
export const config = getConfig().publicRuntimeConfig || {}
|
||||
|
||||
export const assetPrefix = config.assetPrefix || ''
|
||||
export const assetPath = `${assetPrefix}/static/`
|
||||
export const getAssetPath = path => `${assetPath}${path}`
|
||||
|
||||
const ext = /\.mdx?$/
|
||||
export const requirePage = require.context('../pages', true, /\.mdx?$/)
|
||||
export const pathMap = requirePage.keys().reduce((map, key) => {
|
||||
const base = key.replace(ext, '').replace(/\/index$/, '')
|
||||
const path = base.substr(1) // strip the leading "."
|
||||
map[path] = key
|
||||
return map
|
||||
}, {})
|
||||
|
||||
const nested = nest(pathMap)
|
||||
export const pageTree = new TreeModel()
|
||||
export const rootPage = pageTree.parse(nested)
|
||||
|
||||
rootPage.walk(node => {
|
||||
const {model} = node
|
||||
Object.assign(node, model)
|
||||
if (node.file) {
|
||||
const component = requirePage(node.file)
|
||||
node.meta = component.frontMatter || {}
|
||||
node.outline = component.tableOfContents
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('no file for page node:', node.path)
|
||||
}
|
||||
})
|
||||
|
||||
function nest(map) {
|
||||
const nodeMap = {}
|
||||
const nodes = Object.keys(map)
|
||||
.sort()
|
||||
.map(path => {
|
||||
const file = map[path]
|
||||
const keys = path.substr(1).split('/')
|
||||
return (nodeMap[path] = {
|
||||
path,
|
||||
file,
|
||||
isIndex: INDEX_PATTERN.test(file),
|
||||
parent: `/${keys.slice(0, keys.length - 1).join('/')}`,
|
||||
children: []
|
||||
})
|
||||
})
|
||||
|
||||
let root = nodeMap['/']
|
||||
if (!root) {
|
||||
const sorted = nodes.sort((a, b) => a.path.localeCompare(b.path))
|
||||
root = sorted[0]
|
||||
}
|
||||
|
||||
// remove root from the list of nodes
|
||||
nodes.splice(nodes.indexOf(root), 1)
|
||||
|
||||
const rest = []
|
||||
for (const node of nodes) {
|
||||
const parent = nodeMap[node.parent]
|
||||
if (parent) {
|
||||
parent.children.push(node)
|
||||
} else {
|
||||
rest.push(node)
|
||||
}
|
||||
}
|
||||
|
||||
if (rest.length) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('unable to nest some pages:', rest)
|
||||
}
|
||||
|
||||
return root
|
||||
}
|
0
docs/static/.gitkeep
vendored
Normal file
4
docs/static/analytics.js
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'UA-126681523-2');
|
BIN
docs/static/apple-touch-icon.png
vendored
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/static/favicon.png
vendored
Normal file
After Width: | Height: | Size: 73 KiB |
2
docs/static/github/styleguide.css
vendored
Normal file
3
docs/static/github/styleguide.js
vendored
Normal file
BIN
docs/static/typography.png
vendored
Normal file
After Width: | Height: | Size: 4.4 KiB |