完成WACG对比度检测功能。
This commit is contained in:
		| @@ -12,6 +12,7 @@ import { SchemeNotFound } from './pages/SchemeNotFound'; | ||||
| import { Schemes } from './pages/Schemes'; | ||||
| import { TintsShades } from './pages/TintsShades'; | ||||
| import { Tones } from './pages/Tones'; | ||||
| import { WACGCheck } from './pages/WACG'; | ||||
| import { Wheels } from './pages/Wheels'; | ||||
|  | ||||
| const routes = createBrowserRouter([ | ||||
| @@ -35,6 +36,7 @@ const routes = createBrowserRouter([ | ||||
|       { path: 'tints-shades', element: <TintsShades /> }, | ||||
|       { path: 'lighten-darken', element: <LightenDarken /> }, | ||||
|       { path: 'mixer', element: <Mixer /> }, | ||||
|       { path: 'wacg', element: <WACGCheck /> }, | ||||
|     ], | ||||
|   }, | ||||
| ]); | ||||
|   | ||||
							
								
								
									
										13
									
								
								src/page-components/wacg/Ratio.module.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/page-components/wacg/Ratio.module.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| @layer pages { | ||||
|   .ratio_layout { | ||||
|     width: 100%; | ||||
|     display: flex; | ||||
|     flex-direction: row; | ||||
|     justify-content: center; | ||||
|     padding: var(--spacing-s) var(--spacing-m); | ||||
|     .ratio { | ||||
|       font-size: calc(var(--font-size) * 5); | ||||
|       font-weight: bold; | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										13
									
								
								src/page-components/wacg/Ratio.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/page-components/wacg/Ratio.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import styles from './Ratio.module.css'; | ||||
|  | ||||
| type ContrastRatioProps = { | ||||
|   ratio: number; | ||||
| }; | ||||
|  | ||||
| export function ContrastRatio({ ratio }: ContrastRatioProps) { | ||||
|   return ( | ||||
|     <div className={styles.ratio_layout}> | ||||
|       <div className={styles.ratio}>{ratio.toFixed(2)} : 1</div> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										46
									
								
								src/page-components/wacg/TextDemo.module.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/page-components/wacg/TextDemo.module.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| @layer pages { | ||||
|   .demo_block { | ||||
|     border: 1px solid var(--color-border); | ||||
|     border-radius: var(--border-radius-xxs); | ||||
|     padding: var(--spacing-m) var(--spacing-s); | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-items: center; | ||||
|     white-space: nowrap; | ||||
|     gap: var(--spacing-s); | ||||
|     .normal_text { | ||||
|       font-size: 14pt; | ||||
|     } | ||||
|     .large_text { | ||||
|       font-size: 18pt; | ||||
|     } | ||||
|     .bold_text { | ||||
|       font-size: 14pt; | ||||
|       font-weight: bold; | ||||
|     } | ||||
|   } | ||||
|   .wacg_rating { | ||||
|     width: 100%; | ||||
|     display: flex; | ||||
|     flex-direction: row; | ||||
|     align-items: center; | ||||
|     gap: var(--spacing-s); | ||||
|     .rating_unit { | ||||
|       flex: 1; | ||||
|       display: flex; | ||||
|       flex-direction: row; | ||||
|       align-items: center; | ||||
|       gap: var(--spacing-m); | ||||
|     } | ||||
|   } | ||||
|   .sub_header { | ||||
|     display: flex; | ||||
|     flex-direction: row; | ||||
|     align-items: baseline; | ||||
|     gap: var(--spacing-m); | ||||
|   } | ||||
|   .description { | ||||
|     font-size: var(--font-size-xs); | ||||
|     color: var(--color-neutral-focus); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										61
									
								
								src/page-components/wacg/TextDemo.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/page-components/wacg/TextDemo.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| import cx from 'clsx'; | ||||
| import styles from './TextDemo.module.css'; | ||||
|  | ||||
| type TextDemoProps = { | ||||
|   fg: string; | ||||
|   bg: string; | ||||
|   ratio: number; | ||||
| }; | ||||
|  | ||||
| export function TextDemo({ fg, bg, ratio }: TextDemoProps) { | ||||
|   return ( | ||||
|     <> | ||||
|       <header className={styles.sub_header}> | ||||
|         <h5>Normal Text</h5> | ||||
|         <p className={styles.description}>14pt normal weight text.</p> | ||||
|       </header> | ||||
|       <div className={styles.demo_block} style={{ backgroundColor: `#${bg}`, color: `#${fg}` }}> | ||||
|         <div className={styles.normal_text}>The quick brown fox jumps over the lazy dog.</div> | ||||
|         <div className={styles.normal_text}>白日依山尽,黄河入海流。欲穷千里目,更上一层楼。</div> | ||||
|       </div> | ||||
|       <div className={styles.wacg_rating}> | ||||
|         <div className={styles.rating_unit}> | ||||
|           <span>WACG AA:</span> | ||||
|           <span className={cx('badge', 'uppercase', ratio > 4.5 ? 'success' : 'danger')}> | ||||
|             {ratio > 4.5 ? 'pass' : 'failed'} | ||||
|           </span> | ||||
|         </div> | ||||
|         <div className={styles.rating_unit}> | ||||
|           <span>WACG AAA:</span> | ||||
|           <span className={cx('badge', 'uppercase', ratio > 7 ? 'success' : 'danger')}> | ||||
|             {ratio > 7 ? 'pass' : 'failed'} | ||||
|           </span> | ||||
|         </div> | ||||
|       </div> | ||||
|       <header className={styles.sub_header}> | ||||
|         <h5>Large/Bold Text</h5> | ||||
|         <p className={styles.description}>18pt normal weight text and 14pt bold text.</p> | ||||
|       </header> | ||||
|       <div className={styles.demo_block} style={{ backgroundColor: `#${bg}`, color: `#${fg}` }}> | ||||
|         <div className={styles.large_text}>The quick brown fox jumps over the lazy dog.</div> | ||||
|         <div className={styles.large_text}>白日依山尽,黄河入海流。欲穷千里目,更上一层楼。</div> | ||||
|         <div className={styles.bold_text}>The quick brown fox jumps over the lazy dog.</div> | ||||
|         <div className={styles.bold_text}>白日依山尽,黄河入海流。欲穷千里目,更上一层楼。</div> | ||||
|       </div> | ||||
|       <div className={styles.wacg_rating}> | ||||
|         <div className={styles.rating_unit}> | ||||
|           <span>WACG AA:</span> | ||||
|           <span className={cx('badge', 'uppercase', ratio > 3 ? 'success' : 'danger')}> | ||||
|             {ratio > 3 ? 'pass' : 'failed'} | ||||
|           </span> | ||||
|         </div> | ||||
|         <div className={styles.rating_unit}> | ||||
|           <span>WACG AAA:</span> | ||||
|           <span className={cx('badge', 'uppercase', ratio > 4.5 ? 'success' : 'danger')}> | ||||
|             {ratio > 4.5 ? 'pass' : 'failed'} | ||||
|           </span> | ||||
|         </div> | ||||
|       </div> | ||||
|     </> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										40
									
								
								src/pages/WACG.module.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/pages/WACG.module.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| @layer pages { | ||||
|   .wacg_workspace { | ||||
|     flex-direction: column; | ||||
|   } | ||||
|   .explore_section { | ||||
|     width: 100%; | ||||
|     flex: 1; | ||||
|     display: flex; | ||||
|     flex-direction: row; | ||||
|     align-items: stretch; | ||||
|     gap: var(--spacing-m); | ||||
|   } | ||||
|   .function_side { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     gap: var(--spacing-m); | ||||
|     font-size: var(--font-size-s); | ||||
|     .mode_navigation { | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       align-items: stretch; | ||||
|       gap: var(--spacing-s); | ||||
|     } | ||||
|     h5 { | ||||
|       padding-block: var(--spacing-m); | ||||
|       font-size: var(--font-size-m); | ||||
|     } | ||||
|   } | ||||
|   .wacg_content { | ||||
|     flex: 1; | ||||
|     padding: 0 var(--spacing-m); | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     gap: var(--spacing-s); | ||||
|     h5 { | ||||
|       padding-block: var(--spacing-m); | ||||
|       font-size: var(--font-size-m); | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										47
									
								
								src/pages/WACG.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/pages/WACG.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| import cx from 'clsx'; | ||||
| import { useMemo, useState } from 'react'; | ||||
| import { useColorFunction } from '../ColorFunctionContext'; | ||||
| import { ColorPicker } from '../components/ColorPicker'; | ||||
| import { ScrollArea } from '../components/ScrollArea'; | ||||
| import { ContrastRatio } from '../page-components/wacg/Ratio'; | ||||
| import { TextDemo } from '../page-components/wacg/TextDemo'; | ||||
| import styles from './WACG.module.css'; | ||||
|  | ||||
| export function WACGCheck() { | ||||
|   const { colorFn } = useColorFunction(); | ||||
|   const [fgColor, setFgColor] = useState('ffffff'); | ||||
|   const [bgColor, setBgColor] = useState('000000'); | ||||
|   const contrastRatio = useMemo(() => { | ||||
|     try { | ||||
|       if (!colorFn) return 1; | ||||
|       const ratio = colorFn.wacg_relative_contrast(fgColor, bgColor); | ||||
|       return ratio; | ||||
|     } catch (e) { | ||||
|       console.error('[WACG Check]', e); | ||||
|     } | ||||
|     return 1; | ||||
|   }, [fgColor, bgColor]); | ||||
|  | ||||
|   return ( | ||||
|     <div className={cx('workspace', styles.wacg_workspace)}> | ||||
|       <header> | ||||
|         <h3>WACG Check</h3> | ||||
|       </header> | ||||
|       <ScrollArea enableY> | ||||
|         <section className={styles.explore_section}> | ||||
|           <aside className={styles.function_side}> | ||||
|             <h5>Foreground Color</h5> | ||||
|             <ColorPicker color={fgColor} onSelect={setFgColor} /> | ||||
|             <h5>Background Color</h5> | ||||
|             <ColorPicker color={bgColor} onSelect={setBgColor} /> | ||||
|           </aside> | ||||
|           <div className={styles.wacg_content}> | ||||
|             <h5>WACG Contrast Ratio</h5> | ||||
|             <ContrastRatio ratio={contrastRatio} /> | ||||
|             <TextDemo fg={fgColor} bg={bgColor} ratio={contrastRatio} /> | ||||
|           </div> | ||||
|         </section> | ||||
|       </ScrollArea> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user