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 color contrast indicator ``` **Markdown** ```md ![color contrast indicator](https://contrast.now.sh/cff/07c) ``` ## 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 }) => color contrast indicator ``` ## 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" } }