├── bun.lockb ├── .gitignore ├── CHANGELOG.md ├── .github ├── workflows │ ├── lint.yml │ ├── verify.yml │ └── coverage.yml └── FUNDING.yml ├── tsconfig.json ├── LICENSE ├── package.json ├── src ├── chroma.ts └── chroma.spec.ts └── README.md /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kwhitley/itty-chroma/HEAD/bun.lockb -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .claude 3 | .dist 4 | .vscode 5 | CLAUDE.md 6 | node_modules 7 | coverage 8 | dist -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Changelog 2 | 3 | #### v1.0.0 4 | - initial public release, considered stable 5 | #### v0.1.18 6 | - final pre-release 7 | #### v0.1.17 8 | - semifinal README 9 | #### v0.1.16 10 | - updates to README 11 | #### v0.1.15 12 | - mic check... check, check 13 | #### v0.1.x 14 | - test release 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: 4 | push: 5 | branches: [v1.x] 6 | pull_request: 7 | branches: [v1.x] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: oven-sh/setup-bun@v2.0.1 16 | - name: install dependencies 17 | run: bun install 18 | - name: lint 19 | run: bun run lint -------------------------------------------------------------------------------- /.github/workflows/verify.yml: -------------------------------------------------------------------------------- 1 | name: verify 2 | 3 | on: 4 | push: 5 | branches: [v1.x] 6 | pull_request: 7 | branches: [v1.x] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: oven-sh/setup-bun@v2.0.1 16 | - name: Install dependencies 17 | run: bun install 18 | - name: Build 19 | run: bun run build 20 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: kwhitley 4 | open_collective: kevinrwhitley 5 | # patreon: # Replace with a single Patreon username 6 | # ko_fi: # Replace with a single Ko-fi username 7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | # liberapay: # Replace with a single Liberapay username 10 | # issuehunt: # Replace with a single IssueHunt username 11 | # otechie: # Replace with a single Otechie username 12 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Test Coveralls 4 | 5 | jobs: 6 | build: 7 | name: Build 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | with: 12 | fetch-depth: 0 13 | 14 | - uses: oven-sh/setup-bun@v2.0.1 15 | 16 | - name: Clean workspace and run tests 17 | run: | 18 | git clean -xfd # Remove all untracked files, including stale tests 19 | bun install 20 | bun test --coverage --coverage-reporter=lcov 21 | 22 | - name: Coveralls 23 | uses: coverallsapp/github-action@master 24 | with: 25 | github-token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "baseUrl": "src", 6 | "declaration": true, 7 | "sourceMap": true, 8 | "esModuleInterop": true, 9 | "inlineSourceMap": false, 10 | "lib": ["esnext"], 11 | "listEmittedFiles": false, 12 | "listFiles": false, 13 | "noFallthroughCasesInSwitch": true, 14 | "pretty": true, 15 | // "moduleResolution": "nodeNext", // disabled to be compatible with module: "esnext" 16 | // "resolveJsonModule": true, // disabled to be compatible with module: "esnext" 17 | "rootDir": "src", 18 | "skipLibCheck": true, 19 | "strict": true, 20 | "traceResolution": false, 21 | "outDir": "", 22 | "target": "esnext", 23 | "module": "esnext" 24 | }, 25 | "exclude": ["node_modules", "dist", "**/*.spec.ts", "examples"], 26 | "include": ["src", "src/seconds.ts"] 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Kevin R. Whitley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "itty-chroma", 3 | "version": "1.0.6", 4 | "type": "module", 5 | "description": "Powerful styling for the browser console in under 500 bytes.", 6 | "main": "./chroma.js", 7 | "module": "./chroma.mjs", 8 | "types": "./chroma.d.ts", 9 | "exports": { 10 | ".": { 11 | "import": "./chroma.mjs", 12 | "types": "./chroma.d.ts", 13 | "require": "./chroma.cjs" 14 | } 15 | }, 16 | "scripts": { 17 | "dev": "bun test --watch --coverage", 18 | "test": "bun test --coverage", 19 | "lint": "itty lint", 20 | "build": "itty lint && itty build --snippet=chroma --hybrid", 21 | "release": "itty release --prepare --tag --push", 22 | "release:next": "itty release --prepare --tag --push --type=next" 23 | }, 24 | "keywords": [ 25 | "browser", 26 | "console", 27 | "logs", 28 | "color", 29 | "styles", 30 | "minimalist" 31 | ], 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/kwhitley/itty-chroma.git" 35 | }, 36 | "author": "Kevin R. Whitley ", 37 | "license": "MIT", 38 | "bugs": { 39 | "url": "https://github.com/kwhitley/itty-chroma/issues" 40 | }, 41 | "devDependencies": { 42 | "@types/bun": "^1.2.17", 43 | "itty-packager": "^1.6.10" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/chroma.ts: -------------------------------------------------------------------------------- 1 | type StyleFunction = (value: T) => ColoredProxy 2 | 3 | type OutputFunction = (...args: Array) => Array 4 | 5 | type StyleMethods = { 6 | bold: ColoredProxy 7 | italic: ColoredProxy 8 | underline: ColoredProxy 9 | strikethrough: ColoredProxy 10 | font: StyleFunction 11 | size: StyleFunction 12 | bg: StyleFunction 13 | radius: StyleFunction 14 | color: StyleFunction 15 | padding: StyleFunction 16 | border: StyleFunction 17 | style: StyleFunction 18 | log: ColoredProxy 19 | warn: ColoredProxy 20 | error: ColoredProxy 21 | } 22 | 23 | type ColoredProxy = { 24 | [key: string]: ColoredProxy // Allows dynamic colors or methods like "red", "green", etc. 25 | } & StyleMethods & OutputFunction 26 | 27 | let createProxy = ( 28 | styles = '', 29 | which?: string 30 | ): ColoredProxy => 31 | new Proxy( 32 | // @ts-ignore 33 | (...args: any[]) => { 34 | if (!args.length && !styles) return 35 | 36 | let out = [styles], 37 | base = '%c', 38 | wasPadded = styles.match(/pad|dec/), 39 | isPadded: any 40 | 41 | for (let a of args) { 42 | if (a?.zq) a = a() 43 | if (a?.[0]?.startsWith?.('%c')) { 44 | isPadded = a[1].match(/pad|dec/) 45 | if (wasPadded) base = base.slice(0, -1) 46 | if (wasPadded && !isPadded) { 47 | base += '%c ' 48 | out.push('') 49 | } 50 | base += a[0] 51 | out.push(...a.slice(1)) 52 | wasPadded = isPadded 53 | } else { 54 | base += typeof a == 'object' ? '%o ' : '%s ' 55 | out.push(a) 56 | } 57 | } 58 | 59 | return which 60 | // @ts-ignore 61 | ? console[which](base.trim(), ...out) 62 | : [base, ...out] 63 | }, 64 | { 65 | get(_, prop: string) { 66 | let add = (type: string) => 67 | (value: string) => 68 | createProxy(styles + (type ? type + ':' + value : value) + ';', which) 69 | 70 | return prop == 'color' ? add(prop) 71 | : prop == 'bold' ? add('font-weight')(prop) 72 | : prop == 'italic' ? add('font-style')(prop) 73 | : prop == 'underline' ? add('text-decoration')(prop) 74 | : prop == 'strike' ? add('text-decoration')('line-through') 75 | : prop == 'font' ? add('font-family') 76 | : prop == 'size' ? add('font-size') 77 | : prop == 'bg' ? add('background') 78 | : prop == 'radius' ? add('border-radius') 79 | : prop == 'padding' || prop == 'border' ? add(prop) 80 | : prop == 'style' ? add('') 81 | : prop == 'log' || prop == 'warn' || prop == 'error' ? createProxy(styles, prop) 82 | : add('color')(prop) 83 | }, 84 | } 85 | ) as ColoredProxy 86 | 87 | // @ts-ignore 88 | export let chroma: ColoredProxy = createProxy() -------------------------------------------------------------------------------- /src/chroma.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, mock, spyOn } from 'bun:test' 2 | import { chroma } from './chroma' 3 | 4 | type TestLeaf = (args: { 5 | chroma: typeof chroma 6 | log: any, 7 | warn: any, 8 | error: any, 9 | mockFn: typeof mock 10 | }) => void 11 | 12 | type TestTree = { 13 | [key: string]: TestTree | TestLeaf 14 | } 15 | 16 | const isChroma = (instance: any) => typeof instance.a.b.c === 'function' 17 | 18 | const tests: TestTree = { 19 | 'NAMED EXPORTS': { 20 | 'import { chroma } from "itty-chroma"': { 21 | 'is a function': () => expect(typeof chroma).toBe('function'), 22 | 'returns the chroma Proxy': () => expect(isChroma(chroma)).toBe(true), 23 | 'empty execution returns nothing': () => expect(typeof chroma()).toBe('undefined'), 24 | 'returns an infinite Proxy chain': () => expect(typeof chroma.a.b.c.d.e.f).toBe('function'), 25 | 'executing any node returns an array (unless log type specified)': () => { 26 | expect(Array.isArray(chroma.a.b.c('xyz'))).toBe(true) 27 | }, 28 | }, 29 | }, 30 | 'OUTPUT METHODS': { 31 | 'console.log delegation': { 32 | '.log() calls console.log': ({ log }) => { 33 | const result = chroma.a.b.log.d('xyz') 34 | expect(Array.isArray(result)).toBe(false) 35 | // @ts-ignore 36 | expect(result).toBe(undefined) 37 | expect(log).toHaveBeenCalledTimes(1) 38 | }, 39 | }, 40 | 'console.warn delegation': { 41 | '.warn() calls console.warn': ({ warn }) => { 42 | chroma.warn('hey') 43 | expect(warn).toHaveBeenCalledTimes(1) 44 | }, 45 | }, 46 | 'console.error delegation': { 47 | '.error() calls console.error': ({ error }) => { 48 | chroma.error('hey') 49 | expect(error).toHaveBeenCalledTimes(1) 50 | }, 51 | }, 52 | }, 53 | 'STYLE PROPERTIES': { 54 | 'color properties': { 55 | '.color("value") function': () => { 56 | const out = chroma.color('#aaa')() 57 | expect(out.join(' ').indexOf('color:#aaa')).not.toBe(-1) 58 | }, 59 | 'CSS color names work as properties': () => { 60 | const out = chroma.red() 61 | expect(out.join(' ').indexOf('color:red')).not.toBe(-1) 62 | }, 63 | }, 64 | 'text styling': { 65 | '.bold': () => { 66 | const out = chroma.bold() 67 | expect(out.join(' ').indexOf('font-weight:bold')).not.toBe(-1) 68 | }, 69 | '.italic': () => { 70 | const out = chroma.italic() 71 | expect(out.join(' ').indexOf('font-style:italic')).not.toBe(-1) 72 | }, 73 | '.underline': () => { 74 | const out = chroma.underline() 75 | expect(out.join(' ').indexOf('text-decoration:underline')).not.toBe(-1) 76 | }, 77 | '.strike': () => { 78 | const out = chroma.strike() 79 | expect(out.join(' ').indexOf('text-decoration:line-through')).not.toBe(-1) 80 | }, 81 | }, 82 | 'font properties': { 83 | '.font("value")': () => { 84 | const out = chroma.font('Georgia')() 85 | expect(out.join(' ').indexOf('font-family:Georgia')).not.toBe(-1) 86 | }, 87 | '.size("value")': () => { 88 | const out = chroma.size('0.9em')() 89 | expect(out.join(' ').indexOf('font-size:0.9em')).not.toBe(-1) 90 | }, 91 | }, 92 | 'background and layout': { 93 | '.bg("value")': () => { 94 | const out = chroma.bg('rgba(255,0,0,0.3)')() 95 | expect(out.join(' ').indexOf('background:rgba(255,0,0,0.3)')).not.toBe(-1) 96 | }, 97 | '.radius("value")': () => { 98 | const out = chroma.radius('3px')() 99 | expect(out.join(' ').indexOf('border-radius:3px')).not.toBe(-1) 100 | }, 101 | '.padding("value")': () => { 102 | const out = chroma.padding('#aaa')() 103 | expect(out.join(' ').indexOf('padding:#aaa')).not.toBe(-1) 104 | }, 105 | '.border("value")': () => { 106 | const out = chroma.border('1px solid red')() 107 | expect(out.join(' ').indexOf('border:1px solid red')).not.toBe(-1) 108 | }, 109 | }, 110 | 'custom styling': { 111 | '.style() accepts custom CSS': () => { 112 | const styleString = 'text-transform:uppercase;margin-bottom:3rem' 113 | const out = chroma.style(styleString)() 114 | expect(out.join(' ').indexOf(styleString)).not.toBe(-1) 115 | }, 116 | }, 117 | }, 118 | 'BARRIER DETECTION': { 119 | 'padding barriers': { 120 | 'includes no color clear by default': () => { 121 | const result = chroma.red( 122 | 'red text', 123 | chroma.blue('blue text'), 124 | ) 125 | expect(result[0].indexOf('%c%s ')).toBe(0) 126 | expect(result.indexOf('')).toBe(-1) 127 | }, 128 | 'includes a barrier after padding': () => { 129 | const result = chroma.padding('5px')( 130 | 'padded text', 131 | chroma.blue('blue text'), 132 | ) 133 | expect(result[0].indexOf('%c%s%c ')).toBe(0) 134 | expect(result.indexOf('')).toBe(3) 135 | }, 136 | }, 137 | 'decoration barriers': { 138 | 'includes a barrier after text-decoration': () => { 139 | const result = chroma.red( 140 | 'padded text', 141 | chroma.strike, 142 | 'strike text', 143 | chroma.blue, 144 | 'blue text', 145 | ) 146 | expect(result[0]).toBe('%c%s %c%s%c %c%s ') 147 | expect(result.indexOf('')).toBe(5) 148 | }, 149 | }, 150 | }, 151 | 'BEHAVIOR': { 152 | 'function execution': { 153 | 'will not execute non-chroma functions in arguments': ({ mockFn }) => { 154 | chroma.red.log('hello', mockFn, 'world') 155 | 156 | expect(mockFn).not.toHaveBeenCalled() 157 | }, 158 | }, 159 | 'state isolation': { 160 | 'partials do not mutate each other': () => { 161 | const red = chroma.red 162 | 163 | // Use red with additional styles 164 | red.bold.italic('test') 165 | 166 | // red should still only be red, not bold+italic 167 | const result = red('this should only be red') 168 | const styleString = result.join(' ') 169 | 170 | expect(styleString).toContain('color:red') 171 | expect(styleString).not.toContain('font-weight:bold') 172 | expect(styleString).not.toContain('font-style:italic') 173 | }, 174 | }, 175 | }, 176 | } 177 | 178 | // Setup function for each test 179 | const setup = () => { 180 | return { 181 | chroma, 182 | warn: spyOn(console, 'warn').mockImplementation(() => {}), 183 | log: spyOn(console, 'log').mockImplementation(() => {}), 184 | error: spyOn(console, 'error').mockImplementation(() => {}), 185 | mockFn: mock(() => {}), 186 | } 187 | } 188 | 189 | // Recursive test runner 190 | const runTests = (tests: TestTree) => { 191 | for (const [name, test] of Object.entries(tests)) { 192 | if (typeof test === 'function') { 193 | it(name, () => test(setup() as any)) 194 | } else { 195 | describe(name, () => runTests(test)) 196 | } 197 | } 198 | } 199 | 200 | // Run the tests! 201 | runTests(tests) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | 5 | itty-chroma 6 | 7 |

8 | 9 | [![Version](https://img.shields.io/npm/v/itty-chroma.svg?style=flat-square)](https://npmjs.com/package/itty-chroma) 10 | [![Bundle Size](https://deno.bundlejs.com/?q=itty-chroma&badge&badge-style=flat-square)](https://deno.bundlejs.com/?q=itty-chroma) 11 | [![Coverage Status](https://img.shields.io/coveralls/github/kwhitley/itty-chroma?style=flat-square)](https://coveralls.io/github/kwhitley/itty-chroma) 12 | [![Issues](https://img.shields.io/github/issues/kwhitley/itty-chroma?style=flat-square)](https://coveralls.io/github/kwhitley/itty-chroma) 13 | [![Discord](https://img.shields.io/discord/832353585802903572?label=Discord&logo=Discord&style=flat-square&logoColor=fff)](https://discord.gg/53vyrZAu9u) 14 | 15 | ### [v1 Documentation](https://itty.dev/itty-chroma)  |   [Discord](https://discord.gg/53vyrZAu9u) 16 | 17 | --- 18 | 19 | # Easy, flexible styling in the browser console. 20 | ...for the rest of us (and only 500 bytes). 21 | 22 | 23 | 24 | ### Example 25 | ```ts 26 | // keep it simple 27 | chroma.red.log('This will be red.') 28 | 29 | // or play a little 30 | chroma.log( 31 | chroma.green, // set the color to green 32 | 'This is green', 33 | '{ foo: 'bar' }', 34 | chroma.blue.underline.bold, // now switch to blue 35 | 'Now this is blue.', 36 | ) 37 | ``` 38 | ![image](https://github.com/user-attachments/assets/7f84014b-97e1-474f-8020-3430efd3e0c6) 39 | 40 |
41 | 42 | # Quick Start 43 | 44 | ### Option 1: Import 45 | ```ts 46 | import { chroma } from 'itty-chroma' 47 | ``` 48 | 49 | ### Option 2: Just copy this snippet: 50 | 51 | ```ts 52 | let t=(e="",o)=>new Proxy((...t)=>{if(!t.length&&!e)return;let r,i=[e],n="%c",l=e.match(/pad|dec/);for(let e of t)e?.zq&&(e=e()),e?.[0]?.startsWith?.("%c")?(r=e[1].match(/pad|dec/),l&&(n=n.slice(0,-1)),l&&!r&&(n+="%c ",i.push("")),n+=e[0],i.push(...e.slice(1)),l=r):(n+="object"==typeof e?"%o ":"%s ",i.push(e));return o?console[o](n.trim(),...i):[n,...i]},{get(r,i){let n=r=>i=>t(e+(r?r+":"+i:i)+";",o);return"color"==i?n(i):"bold"==i?n("font-weight")(i):"italic"==i?n("font-style")(i):"underline"==i?n("text-decoration")(i):"strike"==i?n("text-decoration")("line-through"):"font"==i?n("font-family"):"size"==i?n("font-size"):"bg"==i?n("background"):"radius"==i?n("border-radius"):"padding"==i||"border"==i?n(i):"style"==i?n(""):"log"==i||"warn"==i||"error"==i?t(e,i):n("color")(i)}}),chroma=t(); 53 | ``` 54 | 55 | _Note: This will lose TypeScript support, but is great for adding to your browser console (via script extensions, etc)._ 56 | 57 |
58 | 59 | # How it Works 60 | 61 | Chroma is just a passthrough for `console`, handling the style assembly, shorthand style props, and chaining. This means you can pass in any number of args, of any type. However, only non-objects (strings, numbers, etc) will be colored. 62 | 63 | If used with `log/warn/error`, it will assemble and print the statement, just like `console.log/warn/error`. If you *don't* include `log/warn/error`, it will output a style token instead. 64 | 65 | With that in mind: 66 | 67 | ### 1. Use `chroma` instead of `console` to print something 68 | ```ts 69 | chroma.log('text') // console.log('text') 70 | chroma.warn('text') // console.warn('text') 71 | chroma.error('text') // console.error('text') 72 | ``` 73 | ![image](https://github.com/user-attachments/assets/0c82f3e9-0fae-4e4a-a021-6d334874ed00) 74 | 75 | 76 | ### 2. Add styles by chaining style properties 77 | ```ts 78 | // call a segment directly, using .log 79 | chroma.bold.red.log('This will be red.') 80 | ``` 81 | ![image](https://github.com/user-attachments/assets/63a78004-87f2-4bf2-ba9e-60407b986419) 82 | 83 | ### 3. Or compose using chroma segments 84 | ```ts 85 | chroma.log( 86 | chroma.bold.green, 87 | 'This will be green.' 88 | ) 89 | ``` 90 | ![image](https://github.com/user-attachments/assets/04a68ebd-3c46-45cc-ad71-9ed8e68b98fc) 91 | 92 | These can be saved for re-use: 93 | ```ts 94 | const blue = chroma.bold.blue 95 | 96 | chroma.log( 97 | blue, 98 | 'This will be blue.' 99 | ) 100 | ``` 101 | ![image](https://github.com/user-attachments/assets/d1083073-f33d-4356-8b21-37ae02fe0d3c) 102 | 103 | They can also be saved with the `.log` as a custom logger: 104 | ```ts 105 | const ittyLogger = chroma.bold.color("#f0c").log 106 | 107 | ittyLogger('This will be itty-colored.') 108 | ``` 109 | ![image](https://github.com/user-attachments/assets/0a2e05aa-923c-4d47-98b8-bf3f583a3cf4) 110 | 111 | ### 4. Any valid CSS color name works (100% support) 112 | ```ts 113 | chroma.salmon.log('This is salmon.') 114 | chroma.cornflowerblue.log('This is cornflowerblue.') 115 | chroma.cornFlowerBlue.log('Case does not matter here...') 116 | ``` 117 | ![image](https://github.com/user-attachments/assets/b363fcec-a289-4f25-af8c-d3d5f31e532f) 118 | 119 | ### 5. All valid CSS works within properties that expect a value 120 | ```ts 121 | chroma 122 | .color('rgba(255,0,100,0.4)') 123 | .log('This works just fine') 124 | ``` 125 | ![image](https://github.com/user-attachments/assets/98f978a0-87b6-4488-8f22-696452e927d0) 126 | 127 | ### 6. ...or use your own custom CSS with `.style(css: string)` 128 | ```ts 129 | chroma 130 | .size('2em') 131 | .padding('0.5em') 132 | .style('text-transform:uppercase; text-shadow:0 0 0.5em magenta;') 133 | .log('So does this') 134 | ``` 135 | ![image](https://github.com/user-attachments/assets/3a6e5bcf-99ab-4616-9794-579c2e0e6cc8) 136 | 137 | ### 7. A style will continue until replaced, or cleared using **`chroma.clear`** 138 | ```ts 139 | chroma.log( 140 | chroma.red('this will be red'), 141 | '...but so will this', 142 | chroma.clear, 143 | 'back to unformatted text' 144 | ) 145 | ``` 146 | ![image](https://github.com/user-attachments/assets/d970e8c1-1249-4a39-a183-845ccd5d841f) 147 | 148 | ### 8. Example: Creating custom log functions 149 | ```ts 150 | // we define a curried function to accept some args now, some later 151 | const createLogger = (type = 'log', label, badge = 'grey', text = 'grey') => 152 | (...args) => 153 | chroma[type]( 154 | chroma.bg(badge).white.bold.padding('2px 5px 1px').radius('0.2rem')(label), 155 | chroma.color(text).italic, 156 | ...args, 157 | ) 158 | 159 | // our loggers are partial executions 160 | const info = createLogger('log', 'INFO', 'green') 161 | const warning = createLogger('warn', 'WARNING', 'orange', 'brown') 162 | 163 | // and we finally call them to log messages 164 | info('This is just a helpful bit of info!') 165 | warning('But this is a more serious warning text...') 166 | ``` 167 | ![image](https://github.com/user-attachments/assets/58cdbcbb-51c3-4b67-8fe8-323bf3a094cd) 168 | 169 |
170 | 171 | # Supported Properties 172 | 173 | | PROPERTY | DESCRIPTION | EXAMPLE(s) | 174 | | --- | --- | --- | 175 | | **.log** | once executed, will output to console.log | `chroma.log('hello')` | 176 | | **.warn** | once executed, will output to console.warn | `chroma.warn('warning text')` | 177 | | **.error** | once executed, will output to console.error | `chroma.error('error text')` | 178 | | **.bold** | bold text | `chroma.bold('this is bold')`, `chroma.bold.red('this is bold and red')` | 179 | | **.italic** | italicized text | `chroma.italic('this is italic')` | 180 | | **.underline** | underlined text | `chroma.underline('text')` | 181 | | **.strike** | text with a line through it | `chroma.strike('this text was removed')` | 182 | | **.{colorName}** | sets text color, supports any valid CSS color name | `chroma.magenta`, `chroma.lightGreen` | 183 | | **.color(value)** | sets font color, supports any valid CSS color | `chroma.color('white')`, `chroma.color('rgba(255,0,0,0.2)')` | 184 | | **.font(value)** | sets font, supports any valid CSS font-family | `chroma.font('Georgia')` | 185 | | **.size(value)** | sets font size | `chroma.size('0.8rem')` | 186 | | **.bg(value)** | sets background, supports any valid CSS background | `chroma.bg('salmon')` | 187 | | **.radius(value)** | sets border-radius (for badges) | `chroma.radius('4px')` | 188 | | **.border(value)** | sets border style | `chroma.border('double 5px red')` | 189 | | **.padding(value)** | sets padding | `chroma.padding('2px 5px')` | 190 | | **.style(value)** | sets custom CSS, allowing any valid sequence | `chroma.style('text-transform:uppercase;text-shadow:0 0 0.5rem rgba(255,0,100,0.5)')` | 191 | | **.none**1 | clears styling for subsequent arguments | `chroma.red('red text', chroma.clear, 'plain text')` | 192 | 193 | 194 | 195 | --------------------------------------------------------------------------------