Repository: jxnblk/contrast-swatch
Branch: master
Commit: 5302fe5f0f3b
Files: 5
Total size: 6.8 KB
Directory structure:
gitextract_uiug9yv8/
├── .gitignore
├── README.md
├── index.js
├── now.json
└── package.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.cache
public
================================================
FILE: README.md
================================================
[![][hero]][hero]
[hero]: https://contrast.now.sh/cff/40f?size=256
# Contrast Swatch
Image microservice for color contrast information
[![][a]][a]
[![][b]][b]
[![][c]][c]
[a]: https://contrast.now.sh/bcf/409
[b]: https://contrast.now.sh/98f/206
[c]: https://contrast.now.sh/fff/40f
## Usage
Contrast swatch images can be used in any place an image is rendered.
The URL accepts a foreground and background color.
[`https://contrast.now.sh/cff/40f`][example]
[example]: https://contrast.now.sh/cff/40f
**HTML**
```html
```
**Markdown**
```md

```
## React
You can wrap the image in a React component (or any templating engine) for generating documentation.
```js
import React from 'react'
export default ({
foreground,
background,
...props
}) =>
```
## RGB
Compare two `rgb` values, or an `rgb` and a hex value:
```
https://contrast.now.sh/rgb(204,255,255)/40f
```
[![][rgb]][rgb]
[rgb]: https://contrast.now.sh/rgb(204,255,255)/40f
## Customization
Use URL queries to customize the styles.
```
https://contrast.now.sh/cff/40f?width=256&height=96&fontSize=1.25
```
**Pass/Fail Label**
[![][pass]][pass]
[![][fail]][fail]
[pass]: https://contrast.now.sh/cff/40f?width=256&height=128&label=1
[fail]: https://contrast.now.sh/a6f/40f?width=256&height=128&label=1
**Font Size**
[![][smallfont]][smallfont]
[![][largefont]][largefont]
[smallfont]: https://contrast.now.sh/cff/40f?width=256&height=128&fontSize=0.5
[largefont]: https://contrast.now.sh/cff/40f?width=256&height=128&fontSize=2
**Size**
[![][large]][large]
[![][small]][small]
[large]: https://contrast.now.sh/cff/40f?size=320
[small]: https://contrast.now.sh/cff/40f?size=48
**Width & Height**
[![][wide]][wide]
[![][tall]][tall]
[wide]: https://contrast.now.sh/cff/40f?width=256&height=48
[tall]: https://contrast.now.sh/cff/40f?width=32&height=48
**Custom Text**
[![][text]][text]
[text]: https://contrast.now.sh/cff/40f?width=256&text=Aa
**Font Weight**
[![][weight]][weight]
[weight]: https://contrast.now.sh/cff/40f?fontWeight=900&width=256
**Radius**
[![][rounded]][rounded]
[![][circle]][circle]
[rounded]: https://contrast.now.sh/cff/40f?radius=8
[circle]: https://contrast.now.sh/cff/40f?radius=48
## Options
Option | Description
---|---
`size` | Width & height in pixels
`width` | Width of image in pixels
`height` | Height of image in pixels (font size will scale based on height)
`fontSize` | Relative font size (default 1)
`fontWeight`| Font weight (default 1)
`label` | Show a pass/fail label based on the [WCAG Criteria][wcag]
`radius` | Border radius
`baseline` | Shift text baseline down
`text` | Render any custom text
## Metadata
A JSON response with color contrast information can be fetched by adding the `type=json` query to the URL.
```
https://contrast.now.sh/cff/40f?type=json
```
**Note:** the returned JSON schema might change in a future version
[wcag]: https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html
## Related
- [Colorable](https://colorable.jxnblk.com)
- [Use Contrast](https://usecontrast.com/)
MIT License
================================================
FILE: index.js
================================================
const Color = require('color')
const { createElement: h } = require('react')
const { renderToStaticMarkup } = require('react-dom/server')
const homepage = 'https://github.com/jxnblk/contrast-swatch'
const parseURL = (req) => {
const { foreground, background, ...query } = req.query
if (!foreground || !background) return null
return {
foreground,
background,
query,
}
}
const HEX = /^[A-Fa-f0-9]{3,6}$/
const getColor = (raw) => {
if (HEX.test(raw)) raw = '#' + raw
try {
return Color(raw)
} catch (e) {
return null
}
}
const getLabel = contrast => {
if (contrast >= 7) return 'AAA'
if (contrast >= 4.5) return 'AA'
if (contrast >= 3) return 'lg'
return 'Fail'
}
const parseColors = (data) => {
const foreground = getColor(data.foreground)
const background = getColor(data.background)
const contrast = foreground.contrast(background)
const label = getLabel(contrast)
return {
foreground,
background,
raw: data,
hex: {
foreground: foreground.hex(),
background: background.hex(),
},
rgb: {
foreground: foreground.rgb().array(),
background: background.rgb().array(),
},
contrast,
label,
}
}
const round = n => Math.floor(100 * n) / 100
const svg = req => {
const data = parseURL(req)
if (!data) return null
const colors = parseColors(data)
const opts = Object.assign({
width: 128,
height: 128,
font: 'system-ui,sans-serif',
fontSize: 1,
fontWeight: 700,
baseline: 0,
label: false,
radius: 0,
}, data.query)
const width = Number(opts.size || opts.width)
const height = Number(opts.size || opts.height)
const xwidth = 32 * (width / height)
const fontSize = Number(opts.fontSize) * 8
let text = [ round(colors.contrast) ]
if (Boolean(opts.label)) {
text.push(colors.label)
}
if (opts.text) text = [opts.text]
const el = h('svg', {
xmlns: 'http://www.w3.org/2000/svg',
width,
height,
viewBox: `0 0 ${xwidth} 32`,
fill: colors.hex.foreground,
style: {
fontFamily: opts.font,
fontWeight: opts.fontWeight,
fontSize,
},
},
h('rect', {
width: xwidth,
height: 32,
fill: colors.hex.background,
rx: opts.radius,
}),
h('text', {
textAnchor: 'middle',
x: xwidth / 2,
y: 16 + Number(opts.baseline),
dominantBaseline: 'middle',
},
text.join(' ')
)
)
const svg = renderToStaticMarkup(el)
return {
...data,
colors,
svg,
}
}
module.exports = async (req, res) => {
const data = svg(req)
if (!data) return
switch (data.query.type) {
case 'json':
return res.send(data)
}
res.setHeader('Content-Type', 'image/svg+xml;charset=utf-8')
res.send(data.svg)
}
================================================
FILE: now.json
================================================
{
"version": 2,
"alias": "contrast.now.sh",
"public": true,
"builds": [
{
"src": "index.js",
"use": "@now/node"
}
],
"routes": [
{
"src": "/",
"status": 302,
"headers": {
"Location": "https://github.com/jxnblk/contrast-swatch"
},
"continue": true
},
{
"src": "/(?[^/]+)/(?[^/]+)",
"dest": "/index.js?foreground=$fg&background=$bg",
"headers": {
"Cache-Control": "max-age=604800"
}
}
]
}
================================================
FILE: package.json
================================================
{
"private": true,
"name": "server",
"version": "0.0.1",
"main": "index.js",
"scripts": {
"dev": "now dev"
},
"dependencies": {
"color": "^3.1.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}