| 1 |
<?php |
| 2 |
// index.php |
| 3 |
date_default_timezone_set('America/Chicago'); |
| 4 |
$start = hrtime(true); |
| 5 |
|
| 6 |
// require_once 'security.php'; |
| 7 |
require_once 'functions.php'; |
| 8 |
require_once 'parse_colors.php'; |
| 9 |
require_once 'process_form.php'; |
| 10 |
require_once 'download_report.php'; |
| 11 |
|
| 12 |
$parsed_colors = []; |
| 13 |
$invalid_colors = []; |
| 14 |
$advanced_mode = false; |
| 15 |
$light_mode_bg_colors = []; |
| 16 |
$light_mode_fg_colors = []; |
| 17 |
$dark_mode_bg_colors = []; |
| 18 |
$dark_mode_fg_colors = []; |
| 19 |
$light_mode_invalid = []; |
| 20 |
$dark_mode_invalid = []; |
| 21 |
$light_mode_excess = false; |
| 22 |
$dark_mode_excess = false; |
| 23 |
$light_mode_duplicates = []; |
| 24 |
$dark_mode_duplicates = []; |
| 25 |
$light_mode_semantic_duplicates = []; |
| 26 |
$dark_mode_semantic_duplicates = []; |
| 27 |
|
| 28 |
// Handle form submission |
| 29 |
if ($_SERVER['REQUEST_METHOD'] === 'POST') { |
| 30 |
$advanced_mode = isset($_POST['advanced_mode']) && $_POST['advanced_mode'] === '1'; |
| 31 |
$contrast_method = $_POST['contrast_method'] ?? 'wcag'; |
| 32 |
|
| 33 |
if (isset($_POST['download'])) { |
| 34 |
if ($advanced_mode && (!empty($_POST['light_text']) || !empty($_POST['light_primary']) || !empty($_POST['light_bg']) || !empty($_POST['dark_text']) || !empty($_POST['dark_primary']) || !empty($_POST['dark_bg']))) { |
| 35 |
// Advanced mode download |
| 36 |
$advanced_result = processAdvancedModeColors( |
| 37 |
$_POST['light_text'] ?? '', |
| 38 |
$_POST['light_primary'] ?? '', |
| 39 |
$_POST['light_bg'] ?? '', |
| 40 |
$_POST['dark_text'] ?? '', |
| 41 |
$_POST['dark_primary'] ?? '', |
| 42 |
$_POST['dark_bg'] ?? '' |
| 43 |
); |
| 44 |
$light_mode_bg_colors = $advanced_result['light_mode']['background_colors']; |
| 45 |
$light_mode_fg_colors = $advanced_result['light_mode']['foreground_colors']; |
| 46 |
$dark_mode_bg_colors = $advanced_result['dark_mode']['background_colors']; |
| 47 |
$dark_mode_fg_colors = $advanced_result['dark_mode']['foreground_colors']; |
| 48 |
|
| 49 |
// Generate combined report |
| 50 |
$timestamp = date('Ymd-His'); |
| 51 |
$method_suffix = $contrast_method === 'apca' ? '-apca' : ($contrast_method === 'both' ? '-both' : '-wcag'); |
| 52 |
$filename = "color-contrast-report-advanced{$method_suffix}-{$timestamp}.html"; |
| 53 |
|
| 54 |
header('Content-Type: text/html'); |
| 55 |
header('Content-Disposition: attachment; filename="' . $filename . '"'); |
| 56 |
|
| 57 |
// Generate report with both modes |
| 58 |
ob_start(); |
| 59 |
include 'report_template_advanced.php'; |
| 60 |
echo ob_get_clean(); |
| 61 |
exit; |
| 62 |
} elseif (!empty($_POST['colors'])) { |
| 63 |
$colors = $_POST['colors']; |
| 64 |
$report = generateColorReport($colors, $contrast_method); |
| 65 |
|
| 66 |
$timestamp = date('Ymd-His'); |
| 67 |
$method_suffix = $contrast_method === 'apca' ? '-apca' : ($contrast_method === 'both' ? '-both' : '-wcag'); |
| 68 |
$filename = "color-contrast-report{$method_suffix}-{$timestamp}.html"; |
| 69 |
|
| 70 |
header('Content-Type: text/html'); |
| 71 |
header('Content-Disposition: attachment; filename="' . $filename . '"'); |
| 72 |
|
| 73 |
echo $report; |
| 74 |
exit; |
| 75 |
} |
| 76 |
} elseif ($advanced_mode && (!empty($_POST['light_text']) || !empty($_POST['light_primary']) || !empty($_POST['light_bg']) || !empty($_POST['dark_text']) || !empty($_POST['dark_primary']) || !empty($_POST['dark_bg']))) { |
| 77 |
// Advanced mode processing |
| 78 |
$advanced_result = processAdvancedModeColors( |
| 79 |
$_POST['light_text'] ?? '', |
| 80 |
$_POST['light_primary'] ?? '', |
| 81 |
$_POST['light_bg'] ?? '', |
| 82 |
$_POST['dark_text'] ?? '', |
| 83 |
$_POST['dark_primary'] ?? '', |
| 84 |
$_POST['dark_bg'] ?? '' |
| 85 |
); |
| 86 |
$light_mode_bg_colors = $advanced_result['light_mode']['background_colors']; |
| 87 |
$light_mode_fg_colors = $advanced_result['light_mode']['foreground_colors']; |
| 88 |
$dark_mode_bg_colors = $advanced_result['dark_mode']['background_colors']; |
| 89 |
$dark_mode_fg_colors = $advanced_result['dark_mode']['foreground_colors']; |
| 90 |
$light_mode_invalid = $advanced_result['light_mode']['invalid_colors']; |
| 91 |
$dark_mode_invalid = $advanced_result['dark_mode']['invalid_colors']; |
| 92 |
$light_mode_excess = $advanced_result['light_mode']['excess_colors']; |
| 93 |
$dark_mode_excess = $advanced_result['dark_mode']['excess_colors']; |
| 94 |
$light_mode_duplicates = $advanced_result['light_mode']['duplicate_colors']; |
| 95 |
$dark_mode_duplicates = $advanced_result['dark_mode']['duplicate_colors']; |
| 96 |
$light_mode_semantic_duplicates = $advanced_result['light_mode']['semantic_duplicates']; |
| 97 |
$dark_mode_semantic_duplicates = $advanced_result['dark_mode']['semantic_duplicates']; |
| 98 |
} elseif (!empty($_POST['colors'])) { |
| 99 |
// Normal mode processing |
| 100 |
$result = processColorForm($_POST['colors']); |
| 101 |
$parsed_colors = $result['parsed_colors']; |
| 102 |
$excess_colors = $result['excess_colors']; |
| 103 |
$invalid_colors = $result['invalid_colors']; |
| 104 |
$duplicate_colors = $result['duplicate_colors']; |
| 105 |
$semantic_duplicates = $result['semantic_duplicates']; |
| 106 |
} |
| 107 |
} |
| 108 |
?> |
| 109 |
<!DOCTYPE html> |
| 110 |
<html lang="en"> |
| 111 |
<head> |
| 112 |
<meta charset="UTF-8"> |
| 113 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 114 |
<title>Jeff's Color Contrast Analyzer</title> |
| 115 |
<link rel="stylesheet" href="style.css"> |
| 116 |
<link rel="apple-touch-icon" sizes="180x180" href="https://contrast.jefftml.com/apple-touch-icon.png"> |
| 117 |
<link rel="icon" type="image/png" sizes="32x32" href="https://contrast.jefftml.com/favicon-32x32.png"> |
| 118 |
<link rel="icon" type="image/png" sizes="16x16" href="https://contrast.jefftml.com/favicon-16x16.png"> |
| 119 |
<link rel="manifest" href="https://contrast.jefftml.com/site.webmanifest"> |
| 120 |
<meta name="description" content="Calculate contrast ratios for multiple colors for WCAG and APCA guidelines. Hex, RGB, HSL, and all CSS color types supported. Download reports for offline use for free."> |
| 121 |
</head> |
| 122 |
<body> |
| 123 |
<nav aria-label="Main"> |
| 124 |
<input type="checkbox" id="toggle" class="menu-toggle"> |
| 125 |
<label for="toggle" class="menu-label">Menu</label> |
| 126 |
<ul class="topnav controlled"> |
| 127 |
<li><a class="active" href="https://contrast.jefftml.com/">Home</a></li> |
| 128 |
<li><a href="https://contrast.jefftml.com/help.html">Help</a></li> |
| 129 |
</ul> |
| 130 |
</nav> |
| 131 |
<main> |
| 132 |
<div class="content"> |
| 133 |
<h1>Jeff’s Color Contrast Analyzer</h1> |
| 134 |
<form method="post"> |
| 135 |
<p>Analyze color contrast between multiple colors, up to <?php echo MAX_COLORS; ?> colors total, in tons of formats. Hex, RGB and RGBA, HSL and HSLA, CSS Named Colors, HSB, HWB, Lab, LCH, Oklab, Oklch, and CMYK are fully supported. CSS syntax is also accepted <code>color: #FFF</code> or <code>background-color: rgb(255, 0, 0);</code>. <a href="https://contrast.jefftml.com/help.html#color-formats">More details and specifications</a> are included in the help documentation.</p> |
| 136 |
<p><code>from</code>, <code>calc()</code>, and <code>color()</code> are not supported. Labels can be added to a color by placing the label before a colon, like <code>link: #2C5491</code></p> |
| 137 |
|
| 138 |
<div style="margin: 1em 0;"> |
| 139 |
<label style="display: block; margin: 0.5em 0;"> |
| 140 |
<input type="checkbox" name="advanced_mode" id="advanced_mode" value="1" <?= (isset($_POST['advanced_mode']) && $_POST['advanced_mode'] === '1') ? 'checked' : '' ?> onchange="toggleAdvancedMode()"> Enable Advanced Mode (Light/Dark Mode) |
| 141 |
</label> |
| 142 |
</div> |
| 143 |
|
| 144 |
<div id="normal_mode_input" style="<?= (isset($_POST['advanced_mode']) && $_POST['advanced_mode'] === '1') ? 'display: none;' : '' ?>"> |
| 145 |
<label for="colors">Input your colors:<br><textarea name="colors" id="colors" <?= (!isset($_POST['advanced_mode']) || $_POST['advanced_mode'] !== '1') ? 'required' : '' ?> autocapitalize="off" autocomplete="off" spellcheck="false" placeholder="Enter colors here, |
| 146 |
one color per line"><?= isset($_POST['colors']) ? htmlspecialchars($_POST['colors']) : '' ?></textarea></label> |
| 147 |
</div> |
| 148 |
|
| 149 |
<div id="advanced_mode_inputs" style="<?= (isset($_POST['advanced_mode']) && $_POST['advanced_mode'] === '1') ? '' : 'display: none;' ?>"> |
| 150 |
<fieldset style="margin: 1em 0; border: 1px solid hsla(0, 0%, 50%, 0.3); padding: 1em;"> |
| 151 |
<legend style="padding: 0 0.5em;">Light Mode Colors</legend> |
| 152 |
<label for="light_text" style="display: block; margin: 0.5em 0;"> |
| 153 |
Text Colors (one per line):<br><textarea name="light_text" id="light_text" autocapitalize="off" autocomplete="off" spellcheck="false" placeholder="e.g., #000000 #1A1A1A" style="width: 100%; min-height: 60px;"><?= isset($_POST['light_text']) ? htmlspecialchars($_POST['light_text']) : '' ?></textarea> |
| 154 |
</label> |
| 155 |
<label for="light_primary" style="display: block; margin: 0.5em 0;"> |
| 156 |
Primary Colors (buttons/links, one per line):<br><textarea name="light_primary" id="light_primary" autocapitalize="off" autocomplete="off" spellcheck="false" placeholder="e.g., #0066CC #0055AA" style="width: 100%; min-height: 60px;"><?= isset($_POST['light_primary']) ? htmlspecialchars($_POST['light_primary']) : '' ?></textarea> |
| 157 |
</label> |
| 158 |
<label for="light_bg" style="display: block; margin: 0.5em 0;"> |
| 159 |
Background Colors (one per line):<br><textarea name="light_bg" id="light_bg" autocapitalize="off" autocomplete="off" spellcheck="false" placeholder="e.g., #FFFFFF #F5F5F5" style="width: 100%; min-height: 60px;"><?= isset($_POST['light_bg']) ? htmlspecialchars($_POST['light_bg']) : '' ?></textarea> |
| 160 |
</label> |
| 161 |
</fieldset> |
| 162 |
|
| 163 |
<fieldset style="margin: 1em 0; border: 1px solid hsla(0, 0%, 50%, 0.3); padding: 1em;"> |
| 164 |
<legend style="padding: 0 0.5em;">Dark Mode Colors</legend> |
| 165 |
<label for="dark_text" style="display: block; margin: 0.5em 0;"> |
| 166 |
Text Colors (one per line):<br><textarea name="dark_text" id="dark_text" autocapitalize="off" autocomplete="off" spellcheck="false" placeholder="e.g., #FFFFFF #F5F5F5" style="width: 100%; min-height: 60px;"><?= isset($_POST['dark_text']) ? htmlspecialchars($_POST['dark_text']) : '' ?></textarea> |
| 167 |
</label> |
| 168 |
<label for="dark_primary" style="display: block; margin: 0.5em 0;"> |
| 169 |
Primary Colors (buttons/links, one per line):<br><textarea name="dark_primary" id="dark_primary" autocapitalize="off" autocomplete="off" spellcheck="false" placeholder="e.g., #66B3FF #5599DD" style="width: 100%; min-height: 60px;"><?= isset($_POST['dark_primary']) ? htmlspecialchars($_POST['dark_primary']) : '' ?></textarea> |
| 170 |
</label> |
| 171 |
<label for="dark_bg" style="display: block; margin: 0.5em 0;"> |
| 172 |
Background Colors (one per line):<br><textarea name="dark_bg" id="dark_bg" autocapitalize="off" autocomplete="off" spellcheck="false" placeholder="e.g., #000000 #1A1A1A" style="width: 100%; min-height: 60px;"><?= isset($_POST['dark_bg']) ? htmlspecialchars($_POST['dark_bg']) : '' ?></textarea> |
| 173 |
</label> |
| 174 |
</fieldset> |
| 175 |
</div> |
| 176 |
|
| 177 |
<div style="margin: 1em 0;"> |
| 178 |
<fieldset style="margin: 0; border: 1px solid hsla(0, 0%, 50%, 0.3); padding: 1em; max-width: 400px;"> |
| 179 |
<legend style="padding: 0 0.5em;">Contrast Method:</legend> |
| 180 |
<label style="display: block; margin: 0.5em 0;"> |
| 181 |
<input type="radio" name="contrast_method" value="wcag" <?= (!isset($contrast_method) || $contrast_method === 'wcag') ? 'checked' : '' ?>> WCAG - current standard |
| 182 |
</label> |
| 183 |
<label style="display: block; margin: 0.5em 0;"> |
| 184 |
<input type="radio" name="contrast_method" value="apca" <?= (isset($contrast_method) && $contrast_method === 'apca') ? 'checked' : '' ?>> APCA - potential standard |
| 185 |
</label> |
| 186 |
<label style="display: block; margin: 0.5em 0;"> |
| 187 |
<input type="radio" name="contrast_method" value="both" <?= (isset($contrast_method) && $contrast_method === 'both') ? 'checked' : '' ?>> Best of Both - combined results |
| 188 |
</label> |
| 189 |
</fieldset> |
| 190 |
</div> |
| 191 |
<button type="submit">Calculate Contrast</button> |
| 192 |
</form> |
| 193 |
<div id="results"></div> |
| 194 |
<?php if (!empty($parsed_colors)): ?> |
| 195 |
<div style="margin-top: 1em;"><form method="post"> |
| 196 |
<input type="hidden" name="colors" value="<?= htmlspecialchars($_POST['colors']) ?>"> |
| 197 |
<input type="hidden" name="contrast_method" value="<?= htmlspecialchars($contrast_method ?? 'wcag') ?>"> |
| 198 |
<button type="submit" name="download" value="1">Download Report</button> |
| 199 |
</form></div> |
| 200 |
<?php endif; |
| 201 |
if ($advanced_mode && (!empty($light_mode_bg_colors) || !empty($dark_mode_bg_colors))): ?> |
| 202 |
<div style="margin-top: 1em;"><form method="post"> |
| 203 |
<input type="hidden" name="advanced_mode" value="1"> |
| 204 |
<input type="hidden" name="light_text" value="<?= htmlspecialchars($_POST['light_text'] ?? '') ?>"> |
| 205 |
<input type="hidden" name="light_primary" value="<?= htmlspecialchars($_POST['light_primary'] ?? '') ?>"> |
| 206 |
<input type="hidden" name="light_bg" value="<?= htmlspecialchars($_POST['light_bg'] ?? '') ?>"> |
| 207 |
<input type="hidden" name="dark_text" value="<?= htmlspecialchars($_POST['dark_text'] ?? '') ?>"> |
| 208 |
<input type="hidden" name="dark_primary" value="<?= htmlspecialchars($_POST['dark_primary'] ?? '') ?>"> |
| 209 |
<input type="hidden" name="dark_bg" value="<?= htmlspecialchars($_POST['dark_bg'] ?? '') ?>"> |
| 210 |
<input type="hidden" name="contrast_method" value="<?= htmlspecialchars($contrast_method ?? 'wcag') ?>"> |
| 211 |
<button type="submit" name="download" value="1">Download Report</button> |
| 212 |
</form></div> |
| 213 |
<?php endif; |
| 214 |
if (!empty($excess_colors)): ?> |
| 215 |
<div class="error-message"> |
| 216 |
<h3>Too Many Colors</h3> |
| 217 |
<p>Only the first <?php echo MAX_COLORS; ?> colors were processed. Please reduce the number of colors in your input.</p> |
| 218 |
</div> |
| 219 |
<br> |
| 220 |
<?php endif; |
| 221 |
if (!empty($invalid_colors)): ?> |
| 222 |
<div class="error-message"> |
| 223 |
<h3>Invalid Colors</h3> |
| 224 |
<p>The following colors could not be parsed.</p> |
| 225 |
<ul class="error-list"> |
| 226 |
<?php foreach ($invalid_colors as $color): ?> |
| 227 |
<li><code><?= htmlspecialchars($color) ?></code></li> |
| 228 |
<?php endforeach; ?> |
| 229 |
</ul> |
| 230 |
</div> |
| 231 |
<br> |
| 232 |
<?php endif; |
| 233 |
if (!empty($duplicate_colors)): ?> |
| 234 |
<div class="warning-message"> |
| 235 |
<h3>Duplicate Colors</h3> |
| 236 |
<p>The following colors were entered more than once. Only the first occurrence was processed.</p> |
| 237 |
<ul class="error-list"> |
| 238 |
<?php foreach ($duplicate_colors as $color): ?> |
| 239 |
<li><code><?= htmlspecialchars($color) ?></code></li> |
| 240 |
<?php endforeach; ?> |
| 241 |
</ul> |
| 242 |
</div> |
| 243 |
<br> |
| 244 |
<?php endif; |
| 245 |
if (!empty($semantic_duplicates)): ?> |
| 246 |
<div class="warning-message"> |
| 247 |
<h3>Equivalent Colors</h3> |
| 248 |
<p>The following colors represent the same values in different formats. Only the first occurrence was processed.</p> |
| 249 |
<ul class="warning-list"> |
| 250 |
<?php foreach ($semantic_duplicates as $group): ?> |
| 251 |
<li> |
| 252 |
Original: <code><?= htmlspecialchars($group['original']) ?></code> |
| 253 |
<br> |
| 254 |
Equivalent formats: |
| 255 |
<?= implode(', ', array_map(function($duplicate) { |
| 256 |
return '<code>' . htmlspecialchars($duplicate) . '</code>'; |
| 257 |
}, $group['duplicates'])) ?> |
| 258 |
</li> |
| 259 |
<?php endforeach; ?> |
| 260 |
</ul> |
| 261 |
</div> |
| 262 |
<br> |
| 263 |
<?php endif; |
| 264 |
// Advanced mode error/warning messages |
| 265 |
if ($advanced_mode && (!empty($light_mode_bg_colors) || !empty($dark_mode_bg_colors))): |
| 266 |
if ($light_mode_excess || $dark_mode_excess): ?> |
| 267 |
<div class="error-message"> |
| 268 |
<h3>Too Many Colors</h3> |
| 269 |
<p>Only the first <?php echo MAX_COLORS; ?> colors were processed in some modes. Please reduce the number of colors in your input.</p> |
| 270 |
</div> |
| 271 |
<br> |
| 272 |
<?php endif; |
| 273 |
if (!empty($light_mode_invalid) || !empty($dark_mode_invalid)): ?> |
| 274 |
<div class="error-message"> |
| 275 |
<h3>Invalid Colors</h3> |
| 276 |
<?php if (!empty($light_mode_invalid)): ?> |
| 277 |
<p>Light mode - The following colors could not be parsed:</p> |
| 278 |
<ul class="error-list"> |
| 279 |
<?php foreach ($light_mode_invalid as $color): ?> |
| 280 |
<li><code><?= htmlspecialchars($color) ?></code></li> |
| 281 |
<?php endforeach; ?> |
| 282 |
</ul> |
| 283 |
<?php endif; |
| 284 |
if (!empty($dark_mode_invalid)): ?> |
| 285 |
<p>Dark mode - The following colors could not be parsed:</p> |
| 286 |
<ul class="error-list"> |
| 287 |
<?php foreach ($dark_mode_invalid as $color): ?> |
| 288 |
<li><code><?= htmlspecialchars($color) ?></code></li> |
| 289 |
<?php endforeach; ?> |
| 290 |
</ul> |
| 291 |
<?php endif; ?> |
| 292 |
</div> |
| 293 |
<br> |
| 294 |
<?php endif; |
| 295 |
if (!empty($light_mode_duplicates) || !empty($dark_mode_duplicates)): ?> |
| 296 |
<div class="warning-message"> |
| 297 |
<h3>Duplicate Colors</h3> |
| 298 |
<p>The following colors were entered more than once. Only the first occurrence was processed.</p> |
| 299 |
<?php if (!empty($light_mode_duplicates)): ?> |
| 300 |
<p>Light mode:</p> |
| 301 |
<ul class="error-list"> |
| 302 |
<?php foreach ($light_mode_duplicates as $color): ?> |
| 303 |
<li><code><?= htmlspecialchars($color) ?></code></li> |
| 304 |
<?php endforeach; ?> |
| 305 |
</ul> |
| 306 |
<?php endif; |
| 307 |
if (!empty($dark_mode_duplicates)): ?> |
| 308 |
<p>Dark mode:</p> |
| 309 |
<ul class="error-list"> |
| 310 |
<?php foreach ($dark_mode_duplicates as $color): ?> |
| 311 |
<li><code><?= htmlspecialchars($color) ?></code></li> |
| 312 |
<?php endforeach; ?> |
| 313 |
</ul> |
| 314 |
<?php endif; ?> |
| 315 |
</div> |
| 316 |
<br> |
| 317 |
<?php endif; |
| 318 |
if (!empty($light_mode_semantic_duplicates) || !empty($dark_mode_semantic_duplicates)): ?> |
| 319 |
<div class="warning-message"> |
| 320 |
<h3>Equivalent Colors</h3> |
| 321 |
<p>The following colors represent the same values in different formats. Only the first occurrence was processed.</p> |
| 322 |
<?php if (!empty($light_mode_semantic_duplicates)): ?> |
| 323 |
<p>Light mode:</p> |
| 324 |
<ul class="warning-list"> |
| 325 |
<?php foreach ($light_mode_semantic_duplicates as $group): ?> |
| 326 |
<li> |
| 327 |
Original: <code><?= htmlspecialchars($group['original']) ?></code> |
| 328 |
<br> |
| 329 |
Equivalent formats: |
| 330 |
<?= implode(', ', array_map(function($duplicate) { |
| 331 |
return '<code>' . htmlspecialchars($duplicate) . '</code>'; |
| 332 |
}, $group['duplicates'])) ?> |
| 333 |
</li> |
| 334 |
<?php endforeach; ?> |
| 335 |
</ul> |
| 336 |
<?php endif; |
| 337 |
if (!empty($dark_mode_semantic_duplicates)): ?> |
| 338 |
<p>Dark mode:</p> |
| 339 |
<ul class="warning-list"> |
| 340 |
<?php foreach ($dark_mode_semantic_duplicates as $group): ?> |
| 341 |
<li> |
| 342 |
Original: <code><?= htmlspecialchars($group['original']) ?></code> |
| 343 |
<br> |
| 344 |
Equivalent formats: |
| 345 |
<?= implode(', ', array_map(function($duplicate) { |
| 346 |
return '<code>' . htmlspecialchars($duplicate) . '</code>'; |
| 347 |
}, $group['duplicates'])) ?> |
| 348 |
</li> |
| 349 |
<?php endforeach; ?> |
| 350 |
</ul> |
| 351 |
<?php endif; ?> |
| 352 |
</div> |
| 353 |
<br> |
| 354 |
<?php endif; |
| 355 |
endif; |
| 356 |
// Normal mode results |
| 357 |
if (!$advanced_mode && !empty($parsed_colors)): |
| 358 |
// log a usage for our stats.txt |
| 359 |
date_default_timezone_set('America/Chicago'); |
| 360 |
$datetime = date('Y-m-d h:i:s A'); |
| 361 |
$file = 'stats.txt'; |
| 362 |
|
| 363 |
// Get IP address (handles proxies too) |
| 364 |
$ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? 'unknown'; |
| 365 |
|
| 366 |
// Get the actual colors (extract just the color values, not labels) |
| 367 |
$color_list = array_keys($parsed_colors); |
| 368 |
$colors_string = implode('|', array_map('htmlspecialchars', $color_list)); |
| 369 |
|
| 370 |
$value = count($parsed_colors)." colors, ".($contrast_method ?? 'wcag')." method, IP: ".$ip_address.", Colors: ".$colors_string; |
| 371 |
|
| 372 |
// Create the log entry |
| 373 |
$logEntry = $datetime . ", " . $value . PHP_EOL; |
| 374 |
file_put_contents($file, $logEntry, FILE_APPEND); |
| 375 |
|
| 376 |
$current_method = $contrast_method ?? 'wcag'; |
| 377 |
?> |
| 378 |
<div class="table-wrapper"> |
| 379 |
<h2>Summary of Compatible Color Combinations (<?= $current_method === 'both' ? 'Both' : strtoupper($current_method) ?>)</h2> |
| 380 |
<table class="checkered"> |
| 381 |
<thead> |
| 382 |
<tr> |
| 383 |
<th>Background</th> |
| 384 |
<?php if ($current_method === 'wcag'): ?> |
| 385 |
<th>AAA ≥ 7.0<br>Best</th> |
| 386 |
<th>AA Normal ≥ 4.5<br>Second Best</th> |
| 387 |
<th>AA Large ≥ 3.0<br>Third Best</th> |
| 388 |
<?php elseif ($current_method === 'apca'): ?> |
| 389 |
<th>Perfect ≥ 90</th> |
| 390 |
<th>Excellent ≥ 75</th> |
| 391 |
<th>Good ≥ 60</th> |
| 392 |
<th>Fair ≥ 45</th> |
| 393 |
<?php else: // both ?> |
| 394 |
<th>Perfect<br>WCAG ≥10 &<br>APCA ≥90</th> |
| 395 |
<th>Excellent<br>WCAG ≥7 &<br>APCA ≥75</th> |
| 396 |
<th>Good<br>WCAG ≥4.5 &<br>APCA ≥60</th> |
| 397 |
<th>Fair<br>WCAG ≥3 &<br>APCA ≥45</th> |
| 398 |
<?php endif; ?> |
| 399 |
</tr> |
| 400 |
</thead> |
| 401 |
<tbody> |
| 402 |
<?php foreach ($parsed_colors as $bg_color => $bg): |
| 403 |
// Calculate all combinations for this background color |
| 404 |
$combinations = []; |
| 405 |
foreach ($parsed_colors as $fg_color => $fg) { |
| 406 |
if ($fg_color === $bg_color) continue; |
| 407 |
|
| 408 |
if ($current_method === 'wcag') { |
| 409 |
// Calculate luminance with alpha blending |
| 410 |
$fg_lum = getLuminance($fg['rgb'], $fg['alpha'], $bg['rgb']); |
| 411 |
$bg_lum = $bg['luminance']; |
| 412 |
$contrast = getContrastRatio($bg_lum, $fg_lum); |
| 413 |
$level = getWCAGLevel($contrast); |
| 414 |
|
| 415 |
if ($level !== 'Fail') { |
| 416 |
$combinations[] = [ |
| 417 |
'color' => $fg_color, |
| 418 |
'contrast' => $contrast, |
| 419 |
'level' => $level |
| 420 |
]; |
| 421 |
} |
| 422 |
} elseif ($current_method === 'apca') { |
| 423 |
// APCA calculations |
| 424 |
$contrast = getAPCAContrast($bg['rgb'], $fg['rgb'], $bg['alpha'], $fg['alpha']); |
| 425 |
$level = getAPCALevel($contrast); |
| 426 |
|
| 427 |
if (!str_contains($level, 'Fail')) { |
| 428 |
$combinations[] = [ |
| 429 |
'color' => $fg_color, |
| 430 |
'contrast' => $contrast, |
| 431 |
'level' => $level |
| 432 |
]; |
| 433 |
} |
| 434 |
} else { // both |
| 435 |
// Calculate both WCAG and APCA |
| 436 |
$fg_lum = getLuminance($fg['rgb'], $fg['alpha'], $bg['rgb']); |
| 437 |
$bg_lum = $bg['luminance']; |
| 438 |
$wcag_contrast = getContrastRatio($bg_lum, $fg_lum); |
| 439 |
$wcag_level = getWCAGLevel($wcag_contrast); |
| 440 |
|
| 441 |
$apca_contrast = getAPCAContrast($bg['rgb'], $fg['rgb'], $bg['alpha'], $fg['alpha']); |
| 442 |
$apca_level = getAPCALevel($apca_contrast); |
| 443 |
|
| 444 |
$combined_level = getCombinedLevel($wcag_contrast, $apca_contrast); |
| 445 |
|
| 446 |
if ($combined_level !== 'Fail') { |
| 447 |
$combinations[] = [ |
| 448 |
'color' => $fg_color, |
| 449 |
'wcag_contrast' => $wcag_contrast, |
| 450 |
'apca_contrast' => $apca_contrast, |
| 451 |
'level' => $combined_level |
| 452 |
]; |
| 453 |
} |
| 454 |
} |
| 455 |
} |
| 456 |
|
| 457 |
if ($current_method === 'wcag') { |
| 458 |
// Group by WCAG level |
| 459 |
$wcag_groups = [ |
| 460 |
'AAA' => [], |
| 461 |
'AA' => [], |
| 462 |
'AA Large' => [] |
| 463 |
]; |
| 464 |
|
| 465 |
foreach ($combinations as $combo) { |
| 466 |
$wcag_groups[$combo['level']][] = $combo; |
| 467 |
} |
| 468 |
|
| 469 |
// Sort each group by contrast ratio |
| 470 |
foreach ($wcag_groups as &$group) { |
| 471 |
usort($group, function($a, $b) { |
| 472 |
return $b['contrast'] <=> $a['contrast']; |
| 473 |
}); |
| 474 |
} |
| 475 |
|
| 476 |
$has_valid_combinations = !empty($wcag_groups['AAA']) || |
| 477 |
!empty($wcag_groups['AA']) || |
| 478 |
!empty($wcag_groups['AA Large']); |
| 479 |
} elseif ($current_method === 'apca') { |
| 480 |
// Group by APCA level |
| 481 |
$apca_groups = [ |
| 482 |
'Perfect' => [], |
| 483 |
'Excellent' => [], |
| 484 |
'Good' => [], |
| 485 |
'Fair' => [] |
| 486 |
]; |
| 487 |
|
| 488 |
foreach ($combinations as $combo) { |
| 489 |
$level_key = explode(' - ', $combo['level'])[0]; |
| 490 |
if (isset($apca_groups[$level_key])) { |
| 491 |
$apca_groups[$level_key][] = $combo; |
| 492 |
} |
| 493 |
} |
| 494 |
|
| 495 |
// Sort each group by absolute contrast value |
| 496 |
foreach ($apca_groups as &$group) { |
| 497 |
usort($group, function($a, $b) { |
| 498 |
return abs($b['contrast']) <=> abs($a['contrast']); |
| 499 |
}); |
| 500 |
} |
| 501 |
|
| 502 |
$has_valid_combinations = !empty($apca_groups['Perfect']) || |
| 503 |
!empty($apca_groups['Excellent']) || |
| 504 |
!empty($apca_groups['Good']) || |
| 505 |
!empty($apca_groups['Fair']); |
| 506 |
} else { // both |
| 507 |
// Group by combined level |
| 508 |
$combined_groups = [ |
| 509 |
'Perfect' => [], |
| 510 |
'Excellent' => [], |
| 511 |
'Good' => [], |
| 512 |
'Fair' => [] |
| 513 |
]; |
| 514 |
|
| 515 |
foreach ($combinations as $combo) { |
| 516 |
if (isset($combined_groups[$combo['level']])) { |
| 517 |
$combined_groups[$combo['level']][] = $combo; |
| 518 |
} |
| 519 |
} |
| 520 |
|
| 521 |
// Sort each group by WCAG contrast ratio |
| 522 |
foreach ($combined_groups as &$group) { |
| 523 |
usort($group, function($a, $b) { |
| 524 |
return $b['wcag_contrast'] <=> $a['wcag_contrast']; |
| 525 |
}); |
| 526 |
} |
| 527 |
|
| 528 |
$has_valid_combinations = !empty($combined_groups['Perfect']) || |
| 529 |
!empty($combined_groups['Excellent']) || |
| 530 |
!empty($combined_groups['Good']) || |
| 531 |
!empty($combined_groups['Fair']); |
| 532 |
} |
| 533 |
?> |
| 534 |
<tr style="background-color: <?= htmlspecialchars(getCssColor($bg_color)) ?>;"> |
| 535 |
<td> |
| 536 |
<?php |
| 537 |
// Use alpha-aware luminance for text color calculation |
| 538 |
$bg_text_lum = getLuminance($bg['rgb'], $bg['alpha']); ?> |
| 539 |
<span style="color: <?= $bg_text_lum > 0.5 ? '#000000' : '#FFFFFF' ?>"><?= htmlspecialchars(getCleanColorName($bg_color)) ?></span> |
| 540 |
</td> |
| 541 |
<?php if ($has_valid_combinations): ?> |
| 542 |
<?php if ($current_method === 'wcag'): ?> |
| 543 |
<?php foreach (['AAA', 'AA', 'AA Large'] as $level): ?> |
| 544 |
<td><?php foreach ($wcag_groups[$level] as $combo): ?> |
| 545 |
<div style="color: <?= htmlspecialchars(getCssColor($combo['color'])) ?>;"><?= htmlspecialchars($combo['color']) ?> (Ratio: <?= number_format($combo['contrast'], 2) ?>)</div> |
| 546 |
<?php endforeach; ?></td> |
| 547 |
<?php endforeach; ?> |
| 548 |
<?php elseif ($current_method === 'apca'): ?> |
| 549 |
<?php foreach (['Perfect', 'Excellent', 'Good', 'Fair'] as $level): ?> |
| 550 |
<td><?php foreach ($apca_groups[$level] as $combo): ?> |
| 551 |
<div style="color: <?= htmlspecialchars(getCssColor($combo['color'])) ?>;"><?= htmlspecialchars($combo['color']) ?> (Lc: <?= number_format($combo['contrast'], 1) ?>)</div> |
| 552 |
<?php endforeach; ?></td> |
| 553 |
<?php endforeach; ?> |
| 554 |
<?php else: // both ?> |
| 555 |
<?php foreach (['Perfect', 'Excellent', 'Good', 'Fair'] as $level): ?> |
| 556 |
<td><?php foreach ($combined_groups[$level] as $combo): ?> |
| 557 |
<div style="color: <?= htmlspecialchars(getCssColor($combo['color'])) ?>;"><?= htmlspecialchars($combo['color']) ?> (<?= number_format($combo['wcag_contrast'], 2) ?>) (Lc <?= number_format($combo['apca_contrast'], 1) ?>)</div> |
| 558 |
<?php endforeach; ?></td> |
| 559 |
<?php endforeach; ?> |
| 560 |
<?php endif; ?> |
| 561 |
<?php else: ?> |
| 562 |
<td colspan="<?= $current_method === 'wcag' ? '3' : '4' ?>" style="text-align: center; color: <?= $bg_text_lum > 0.5 ? '#000000' : '#FFFFFF' ?>"> |
| 563 |
<?php if ($current_method === 'wcag'): ?> |
| 564 |
No valid color combinations found (all contrast ratios below 3.0) |
| 565 |
<?php elseif ($current_method === 'apca'): ?> |
| 566 |
No valid color combinations found (all APCA values below 45) |
| 567 |
<?php else: ?> |
| 568 |
No valid color combinations found (failed both WCAG and APCA thresholds) |
| 569 |
<?php endif; ?> |
| 570 |
</td> |
| 571 |
<?php endif; ?> |
| 572 |
</tr> |
| 573 |
<?php endforeach; ?> |
| 574 |
</tbody> |
| 575 |
</table> |
| 576 |
</div> |
| 577 |
|
| 578 |
<div class="table-wrapper"> |
| 579 |
<h2>Complete Contrast Grid (<?= $current_method === 'both' ? 'Both' : strtoupper($current_method) ?>)</h2> |
| 580 |
<table class="checkered"> |
| 581 |
<tr> |
| 582 |
<th>BG \ FG</th> |
| 583 |
<?php foreach ($parsed_colors as $color => $data): ?> |
| 584 |
<th><?= htmlspecialchars(getCleanColorName($color)) ?></th> |
| 585 |
<?php endforeach; ?> |
| 586 |
</tr> |
| 587 |
<?php foreach ($parsed_colors as $bg_color => $bg_data): ?> |
| 588 |
<tr> |
| 589 |
<th><?= htmlspecialchars(getCleanColorName($bg_color)) ?></th> |
| 590 |
<?php foreach ($parsed_colors as $fg_color => $fg_data): |
| 591 |
if ($current_method === 'wcag') { |
| 592 |
// Calculate luminance with alpha blending |
| 593 |
$fg_lum = getLuminance($fg_data['rgb'], $fg_data['alpha'], $bg_data['rgb']); |
| 594 |
$bg_lum = $bg_data['luminance']; |
| 595 |
$contrast = getContrastRatio($bg_lum, $fg_lum); |
| 596 |
$level = getWCAGLevel($contrast); |
| 597 |
$display_value = number_format($contrast, 2); |
| 598 |
} elseif ($current_method === 'apca') { |
| 599 |
// APCA calculations |
| 600 |
$contrast = getAPCAContrast($bg_data['rgb'], $fg_data['rgb'], $bg_data['alpha'], $fg_data['alpha']); |
| 601 |
$level = getAPCALevel($contrast); |
| 602 |
$display_value = number_format($contrast, 1); |
| 603 |
} else { // both |
| 604 |
// Calculate both WCAG and APCA |
| 605 |
$fg_lum = getLuminance($fg_data['rgb'], $fg_data['alpha'], $bg_data['rgb']); |
| 606 |
$bg_lum = $bg_data['luminance']; |
| 607 |
$wcag_contrast = getContrastRatio($bg_lum, $fg_lum); |
| 608 |
$wcag_level = getWCAGLevel($wcag_contrast); |
| 609 |
|
| 610 |
$apca_contrast = getAPCAContrast($bg_data['rgb'], $fg_data['rgb'], $bg_data['alpha'], $fg_data['alpha']); |
| 611 |
$apca_level = getAPCALevel($apca_contrast); |
| 612 |
|
| 613 |
$combined_level = getCombinedLevel($wcag_contrast, $apca_contrast); |
| 614 |
} |
| 615 |
?> |
| 616 |
<td style="background-color: <?= htmlspecialchars(getCssColor($bg_color)) ?>;"> |
| 617 |
<!-- color value hover / alt text? --> |
| 618 |
<div class="sample-text" style="color: <?= htmlspecialchars(getCssColor($fg_color)) ?>;">Sample |
| 619 |
<div style="font-size: 0.8em;"> |
| 620 |
<?php if ($current_method === 'wcag'): ?> |
| 621 |
<?= $display_value ?><br><?= $level ?> |
| 622 |
<?php elseif ($current_method === 'apca'): ?> |
| 623 |
Lc <?= $display_value ?><br><?= explode(' - ', $level)[0] ?> |
| 624 |
<?php else: // both ?> |
| 625 |
(<?= number_format($wcag_contrast, 2) ?>) (Lc <?= number_format($apca_contrast, 1) ?>)<br><?= $combined_level ?> |
| 626 |
<?php endif; ?> |
| 627 |
</div> |
| 628 |
</div> |
| 629 |
</td> |
| 630 |
<?php endforeach; ?> |
| 631 |
</tr> |
| 632 |
<?php endforeach; ?> |
| 633 |
</table> |
| 634 |
</div> |
| 635 |
<?php endif; |
| 636 |
// Advanced mode results - Light Mode |
| 637 |
if ($advanced_mode && !empty($light_mode_bg_colors)): |
| 638 |
$current_method = $contrast_method ?? 'wcag'; |
| 639 |
$bg_colors = $light_mode_bg_colors; |
| 640 |
$fg_colors = $light_mode_fg_colors; |
| 641 |
?> |
| 642 |
<h2 style="margin-top: 2em;">Light Mode Results</h2> |
| 643 |
<div class="table-wrapper"> |
| 644 |
<h2>Summary of Compatible Color Combinations (<?= $current_method === 'both' ? 'Both' : strtoupper($current_method) ?>)</h2> |
| 645 |
<table class="checkered"> |
| 646 |
<thead> |
| 647 |
<tr> |
| 648 |
<th>Background</th> |
| 649 |
<?php if ($current_method === 'wcag'): ?> |
| 650 |
<th>AAA ≥ 7.0<br>Best</th> |
| 651 |
<th>AA Normal ≥ 4.5<br>Second Best</th> |
| 652 |
<th>AA Large ≥ 3.0<br>Third Best</th> |
| 653 |
<?php elseif ($current_method === 'apca'): ?> |
| 654 |
<th>Perfect ≥ 90</th> |
| 655 |
<th>Excellent ≥ 75</th> |
| 656 |
<th>Good ≥ 60</th> |
| 657 |
<th>Fair ≥ 45</th> |
| 658 |
<?php else: // both ?> |
| 659 |
<th>Perfect<br>WCAG ≥10 &<br>APCA ≥90</th> |
| 660 |
<th>Excellent<br>WCAG ≥7 &<br>APCA ≥75</th> |
| 661 |
<th>Good<br>WCAG ≥4.5 &<br>APCA ≥60</th> |
| 662 |
<th>Fair<br>WCAG ≥3 &<br>APCA ≥45</th> |
| 663 |
<?php endif; ?> |
| 664 |
</tr> |
| 665 |
</thead> |
| 666 |
<tbody> |
| 667 |
<?php foreach ($bg_colors as $bg_color => $bg): |
| 668 |
// Calculate all combinations for this background color |
| 669 |
$combinations = []; |
| 670 |
foreach ($fg_colors as $fg_color => $fg) { |
| 671 |
|
| 672 |
if ($current_method === 'wcag') { |
| 673 |
// Calculate luminance with alpha blending |
| 674 |
$fg_lum = getLuminance($fg['rgb'], $fg['alpha'], $bg['rgb']); |
| 675 |
$bg_lum = $bg['luminance']; |
| 676 |
$contrast = getContrastRatio($bg_lum, $fg_lum); |
| 677 |
$level = getWCAGLevel($contrast); |
| 678 |
|
| 679 |
if ($level !== 'Fail') { |
| 680 |
$combinations[] = [ |
| 681 |
'color' => $fg_color, |
| 682 |
'contrast' => $contrast, |
| 683 |
'level' => $level |
| 684 |
]; |
| 685 |
} |
| 686 |
} elseif ($current_method === 'apca') { |
| 687 |
// APCA calculations |
| 688 |
$contrast = getAPCAContrast($bg['rgb'], $fg['rgb'], $bg['alpha'], $fg['alpha']); |
| 689 |
$level = getAPCALevel($contrast); |
| 690 |
|
| 691 |
if (!str_contains($level, 'Fail')) { |
| 692 |
$combinations[] = [ |
| 693 |
'color' => $fg_color, |
| 694 |
'contrast' => $contrast, |
| 695 |
'level' => $level |
| 696 |
]; |
| 697 |
} |
| 698 |
} else { // both |
| 699 |
// Calculate both WCAG and APCA |
| 700 |
$fg_lum = getLuminance($fg['rgb'], $fg['alpha'], $bg['rgb']); |
| 701 |
$bg_lum = $bg['luminance']; |
| 702 |
$wcag_contrast = getContrastRatio($bg_lum, $fg_lum); |
| 703 |
$wcag_level = getWCAGLevel($wcag_contrast); |
| 704 |
|
| 705 |
$apca_contrast = getAPCAContrast($bg['rgb'], $fg['rgb'], $bg['alpha'], $fg['alpha']); |
| 706 |
$apca_level = getAPCALevel($apca_contrast); |
| 707 |
|
| 708 |
$combined_level = getCombinedLevel($wcag_contrast, $apca_contrast); |
| 709 |
|
| 710 |
if ($combined_level !== 'Fail') { |
| 711 |
$combinations[] = [ |
| 712 |
'color' => $fg_color, |
| 713 |
'wcag_contrast' => $wcag_contrast, |
| 714 |
'apca_contrast' => $apca_contrast, |
| 715 |
'level' => $combined_level |
| 716 |
]; |
| 717 |
} |
| 718 |
} |
| 719 |
} |
| 720 |
|
| 721 |
if ($current_method === 'wcag') { |
| 722 |
// Group by WCAG level |
| 723 |
$wcag_groups = [ |
| 724 |
'AAA' => [], |
| 725 |
'AA' => [], |
| 726 |
'AA Large' => [] |
| 727 |
]; |
| 728 |
|
| 729 |
foreach ($combinations as $combo) { |
| 730 |
$wcag_groups[$combo['level']][] = $combo; |
| 731 |
} |
| 732 |
|
| 733 |
// Sort each group by contrast ratio |
| 734 |
foreach ($wcag_groups as &$group) { |
| 735 |
usort($group, function($a, $b) { |
| 736 |
return $b['contrast'] <=> $a['contrast']; |
| 737 |
}); |
| 738 |
} |
| 739 |
|
| 740 |
$has_valid_combinations = !empty($wcag_groups['AAA']) || |
| 741 |
!empty($wcag_groups['AA']) || |
| 742 |
!empty($wcag_groups['AA Large']); |
| 743 |
} elseif ($current_method === 'apca') { |
| 744 |
// Group by APCA level |
| 745 |
$apca_groups = [ |
| 746 |
'Perfect' => [], |
| 747 |
'Excellent' => [], |
| 748 |
'Good' => [], |
| 749 |
'Fair' => [] |
| 750 |
]; |
| 751 |
|
| 752 |
foreach ($combinations as $combo) { |
| 753 |
$level_key = explode(' - ', $combo['level'])[0]; |
| 754 |
if (isset($apca_groups[$level_key])) { |
| 755 |
$apca_groups[$level_key][] = $combo; |
| 756 |
} |
| 757 |
} |
| 758 |
|
| 759 |
// Sort each group by absolute contrast value |
| 760 |
foreach ($apca_groups as &$group) { |
| 761 |
usort($group, function($a, $b) { |
| 762 |
return abs($b['contrast']) <=> abs($a['contrast']); |
| 763 |
}); |
| 764 |
} |
| 765 |
|
| 766 |
$has_valid_combinations = !empty($apca_groups['Perfect']) || |
| 767 |
!empty($apca_groups['Excellent']) || |
| 768 |
!empty($apca_groups['Good']) || |
| 769 |
!empty($apca_groups['Fair']); |
| 770 |
} else { // both |
| 771 |
// Group by combined level |
| 772 |
$combined_groups = [ |
| 773 |
'Perfect' => [], |
| 774 |
'Excellent' => [], |
| 775 |
'Good' => [], |
| 776 |
'Fair' => [] |
| 777 |
]; |
| 778 |
|
| 779 |
foreach ($combinations as $combo) { |
| 780 |
if (isset($combined_groups[$combo['level']])) { |
| 781 |
$combined_groups[$combo['level']][] = $combo; |
| 782 |
} |
| 783 |
} |
| 784 |
|
| 785 |
// Sort each group by WCAG contrast ratio |
| 786 |
foreach ($combined_groups as &$group) { |
| 787 |
usort($group, function($a, $b) { |
| 788 |
return $b['wcag_contrast'] <=> $a['wcag_contrast']; |
| 789 |
}); |
| 790 |
} |
| 791 |
|
| 792 |
$has_valid_combinations = !empty($combined_groups['Perfect']) || |
| 793 |
!empty($combined_groups['Excellent']) || |
| 794 |
!empty($combined_groups['Good']) || |
| 795 |
!empty($combined_groups['Fair']); |
| 796 |
} |
| 797 |
?> |
| 798 |
<tr style="background-color: <?= htmlspecialchars(getCssColor($bg_color)) ?>;"> |
| 799 |
<td> |
| 800 |
<?php |
| 801 |
// Use alpha-aware luminance for text color calculation |
| 802 |
$bg_text_lum = getLuminance($bg['rgb'], $bg['alpha']); ?> |
| 803 |
<span style="color: <?= $bg_text_lum > 0.5 ? '#000000' : '#FFFFFF' ?>"><?= htmlspecialchars(getCleanColorName($bg_color)) ?></span> |
| 804 |
</td> |
| 805 |
<?php if ($has_valid_combinations): ?> |
| 806 |
<?php if ($current_method === 'wcag'): ?> |
| 807 |
<?php foreach (['AAA', 'AA', 'AA Large'] as $level): ?> |
| 808 |
<td><?php foreach ($wcag_groups[$level] as $combo): ?> |
| 809 |
<div style="color: <?= htmlspecialchars(getCssColor($combo['color'])) ?>;"><?= htmlspecialchars($combo['color']) ?> (Ratio: <?= number_format($combo['contrast'], 2) ?>)</div> |
| 810 |
<?php endforeach; ?></td> |
| 811 |
<?php endforeach; ?> |
| 812 |
<?php elseif ($current_method === 'apca'): ?> |
| 813 |
<?php foreach (['Perfect', 'Excellent', 'Good', 'Fair'] as $level): ?> |
| 814 |
<td><?php foreach ($apca_groups[$level] as $combo): ?> |
| 815 |
<div style="color: <?= htmlspecialchars(getCssColor($combo['color'])) ?>;"><?= htmlspecialchars($combo['color']) ?> (Lc: <?= number_format($combo['contrast'], 1) ?>)</div> |
| 816 |
<?php endforeach; ?></td> |
| 817 |
<?php endforeach; ?> |
| 818 |
<?php else: // both ?> |
| 819 |
<?php foreach (['Perfect', 'Excellent', 'Good', 'Fair'] as $level): ?> |
| 820 |
<td><?php foreach ($combined_groups[$level] as $combo): ?> |
| 821 |
<div style="color: <?= htmlspecialchars(getCssColor($combo['color'])) ?>;"><?= htmlspecialchars($combo['color']) ?> (<?= number_format($combo['wcag_contrast'], 2) ?>) (Lc <?= number_format($combo['apca_contrast'], 1) ?>)</div> |
| 822 |
<?php endforeach; ?></td> |
| 823 |
<?php endforeach; ?> |
| 824 |
<?php endif; ?> |
| 825 |
<?php else: ?> |
| 826 |
<td colspan="<?= $current_method === 'wcag' ? '3' : '4' ?>" style="text-align: center; color: <?= $bg_text_lum > 0.5 ? '#000000' : '#FFFFFF' ?>"> |
| 827 |
<?php if ($current_method === 'wcag'): ?> |
| 828 |
No valid color combinations found (all contrast ratios below 3.0) |
| 829 |
<?php elseif ($current_method === 'apca'): ?> |
| 830 |
No valid color combinations found (all APCA values below 45) |
| 831 |
<?php else: ?> |
| 832 |
No valid color combinations found (failed both WCAG and APCA thresholds) |
| 833 |
<?php endif; ?> |
| 834 |
</td> |
| 835 |
<?php endif; ?> |
| 836 |
</tr> |
| 837 |
<?php endforeach; ?> |
| 838 |
</tbody> |
| 839 |
</table> |
| 840 |
</div> |
| 841 |
|
| 842 |
<div class="table-wrapper"> |
| 843 |
<h2>Complete Contrast Grid - Light Mode (<?= $current_method === 'both' ? 'Both' : strtoupper($current_method) ?>)</h2> |
| 844 |
<table class="checkered"> |
| 845 |
<tr> |
| 846 |
<th>BG \ FG</th> |
| 847 |
<?php foreach ($fg_colors as $color => $data): ?> |
| 848 |
<th><?= htmlspecialchars(getCleanColorName($color)) ?></th> |
| 849 |
<?php endforeach; ?> |
| 850 |
</tr> |
| 851 |
<?php foreach ($bg_colors as $bg_color => $bg_data): ?> |
| 852 |
<tr> |
| 853 |
<th><?= htmlspecialchars(getCleanColorName($bg_color)) ?></th> |
| 854 |
<?php foreach ($fg_colors as $fg_color => $fg_data): |
| 855 |
if ($current_method === 'wcag') { |
| 856 |
// Calculate luminance with alpha blending |
| 857 |
$fg_lum = getLuminance($fg_data['rgb'], $fg_data['alpha'], $bg_data['rgb']); |
| 858 |
$bg_lum = $bg_data['luminance']; |
| 859 |
$contrast = getContrastRatio($bg_lum, $fg_lum); |
| 860 |
$level = getWCAGLevel($contrast); |
| 861 |
$display_value = number_format($contrast, 2); |
| 862 |
} elseif ($current_method === 'apca') { |
| 863 |
// APCA calculations |
| 864 |
$contrast = getAPCAContrast($bg_data['rgb'], $fg_data['rgb'], $bg_data['alpha'], $fg_data['alpha']); |
| 865 |
$level = getAPCALevel($contrast); |
| 866 |
$display_value = number_format($contrast, 1); |
| 867 |
} else { // both |
| 868 |
// Calculate both WCAG and APCA |
| 869 |
$fg_lum = getLuminance($fg_data['rgb'], $fg_data['alpha'], $bg_data['rgb']); |
| 870 |
$bg_lum = $bg_data['luminance']; |
| 871 |
$wcag_contrast = getContrastRatio($bg_lum, $fg_lum); |
| 872 |
$wcag_level = getWCAGLevel($wcag_contrast); |
| 873 |
|
| 874 |
$apca_contrast = getAPCAContrast($bg_data['rgb'], $fg_data['rgb'], $bg_data['alpha'], $fg_data['alpha']); |
| 875 |
$apca_level = getAPCALevel($apca_contrast); |
| 876 |
|
| 877 |
$combined_level = getCombinedLevel($wcag_contrast, $apca_contrast); |
| 878 |
} |
| 879 |
?> |
| 880 |
<td style="background-color: <?= htmlspecialchars(getCssColor($bg_color)) ?>;"> |
| 881 |
<!-- color value hover / alt text? --> |
| 882 |
<div class="sample-text" style="color: <?= htmlspecialchars(getCssColor($fg_color)) ?>;">Sample |
| 883 |
<div style="font-size: 0.8em;"> |
| 884 |
<?php if ($current_method === 'wcag'): ?> |
| 885 |
<?= $display_value ?><br><?= $level ?> |
| 886 |
<?php elseif ($current_method === 'apca'): ?> |
| 887 |
Lc <?= $display_value ?><br><?= explode(' - ', $level)[0] ?> |
| 888 |
<?php else: // both ?> |
| 889 |
(<?= number_format($wcag_contrast, 2) ?>) (Lc <?= number_format($apca_contrast, 1) ?>)<br><?= $combined_level ?> |
| 890 |
<?php endif; ?> |
| 891 |
</div> |
| 892 |
</div> |
| 893 |
</td> |
| 894 |
<?php endforeach; ?> |
| 895 |
</tr> |
| 896 |
<?php endforeach; ?> |
| 897 |
</table> |
| 898 |
</div> |
| 899 |
<?php endif; |
| 900 |
// Advanced mode results - Dark Mode |
| 901 |
if ($advanced_mode && !empty($dark_mode_bg_colors)): |
| 902 |
$current_method = $contrast_method ?? 'wcag'; |
| 903 |
$bg_colors = $dark_mode_bg_colors; |
| 904 |
$fg_colors = $dark_mode_fg_colors; |
| 905 |
?> |
| 906 |
<h2 style="margin-top: 2em;">Dark Mode Results</h2> |
| 907 |
<div class="table-wrapper"> |
| 908 |
<h2>Summary of Compatible Color Combinations (<?= $current_method === 'both' ? 'Both' : strtoupper($current_method) ?>)</h2> |
| 909 |
<table class="checkered"> |
| 910 |
<thead> |
| 911 |
<tr> |
| 912 |
<th>Background</th> |
| 913 |
<?php if ($current_method === 'wcag'): ?> |
| 914 |
<th>AAA ≥ 7.0<br>Best</th> |
| 915 |
<th>AA Normal ≥ 4.5<br>Second Best</th> |
| 916 |
<th>AA Large ≥ 3.0<br>Third Best</th> |
| 917 |
<?php elseif ($current_method === 'apca'): ?> |
| 918 |
<th>Perfect ≥ 90</th> |
| 919 |
<th>Excellent ≥ 75</th> |
| 920 |
<th>Good ≥ 60</th> |
| 921 |
<th>Fair ≥ 45</th> |
| 922 |
<?php else: // both ?> |
| 923 |
<th>Perfect<br>WCAG ≥10 &<br>APCA ≥90</th> |
| 924 |
<th>Excellent<br>WCAG ≥7 &<br>APCA ≥75</th> |
| 925 |
<th>Good<br>WCAG ≥4.5 &<br>APCA ≥60</th> |
| 926 |
<th>Fair<br>WCAG ≥3 &<br>APCA ≥45</th> |
| 927 |
<?php endif; ?> |
| 928 |
</tr> |
| 929 |
</thead> |
| 930 |
<tbody> |
| 931 |
<?php foreach ($bg_colors as $bg_color => $bg): |
| 932 |
// Calculate all combinations for this background color |
| 933 |
$combinations = []; |
| 934 |
foreach ($fg_colors as $fg_color => $fg) { |
| 935 |
|
| 936 |
if ($current_method === 'wcag') { |
| 937 |
// Calculate luminance with alpha blending |
| 938 |
$fg_lum = getLuminance($fg['rgb'], $fg['alpha'], $bg['rgb']); |
| 939 |
$bg_lum = $bg['luminance']; |
| 940 |
$contrast = getContrastRatio($bg_lum, $fg_lum); |
| 941 |
$level = getWCAGLevel($contrast); |
| 942 |
|
| 943 |
if ($level !== 'Fail') { |
| 944 |
$combinations[] = [ |
| 945 |
'color' => $fg_color, |
| 946 |
'contrast' => $contrast, |
| 947 |
'level' => $level |
| 948 |
]; |
| 949 |
} |
| 950 |
} elseif ($current_method === 'apca') { |
| 951 |
// APCA calculations |
| 952 |
$contrast = getAPCAContrast($bg['rgb'], $fg['rgb'], $bg['alpha'], $fg['alpha']); |
| 953 |
$level = getAPCALevel($contrast); |
| 954 |
|
| 955 |
if (!str_contains($level, 'Fail')) { |
| 956 |
$combinations[] = [ |
| 957 |
'color' => $fg_color, |
| 958 |
'contrast' => $contrast, |
| 959 |
'level' => $level |
| 960 |
]; |
| 961 |
} |
| 962 |
} else { // both |
| 963 |
// Calculate both WCAG and APCA |
| 964 |
$fg_lum = getLuminance($fg['rgb'], $fg['alpha'], $bg['rgb']); |
| 965 |
$bg_lum = $bg['luminance']; |
| 966 |
$wcag_contrast = getContrastRatio($bg_lum, $fg_lum); |
| 967 |
$wcag_level = getWCAGLevel($wcag_contrast); |
| 968 |
|
| 969 |
$apca_contrast = getAPCAContrast($bg['rgb'], $fg['rgb'], $bg['alpha'], $fg['alpha']); |
| 970 |
$apca_level = getAPCALevel($apca_contrast); |
| 971 |
|
| 972 |
$combined_level = getCombinedLevel($wcag_contrast, $apca_contrast); |
| 973 |
|
| 974 |
if ($combined_level !== 'Fail') { |
| 975 |
$combinations[] = [ |
| 976 |
'color' => $fg_color, |
| 977 |
'wcag_contrast' => $wcag_contrast, |
| 978 |
'apca_contrast' => $apca_contrast, |
| 979 |
'level' => $combined_level |
| 980 |
]; |
| 981 |
} |
| 982 |
} |
| 983 |
} |
| 984 |
|
| 985 |
if ($current_method === 'wcag') { |
| 986 |
// Group by WCAG level |
| 987 |
$wcag_groups = [ |
| 988 |
'AAA' => [], |
| 989 |
'AA' => [], |
| 990 |
'AA Large' => [] |
| 991 |
]; |
| 992 |
|
| 993 |
foreach ($combinations as $combo) { |
| 994 |
$wcag_groups[$combo['level']][] = $combo; |
| 995 |
} |
| 996 |
|
| 997 |
// Sort each group by contrast ratio |
| 998 |
foreach ($wcag_groups as &$group) { |
| 999 |
usort($group, function($a, $b) { |
| 1000 |
return $b['contrast'] <=> $a['contrast']; |
| 1001 |
}); |
| 1002 |
} |
| 1003 |
|
| 1004 |
$has_valid_combinations = !empty($wcag_groups['AAA']) || |
| 1005 |
!empty($wcag_groups['AA']) || |
| 1006 |
!empty($wcag_groups['AA Large']); |
| 1007 |
} elseif ($current_method === 'apca') { |
| 1008 |
// Group by APCA level |
| 1009 |
$apca_groups = [ |
| 1010 |
'Perfect' => [], |
| 1011 |
'Excellent' => [], |
| 1012 |
'Good' => [], |
| 1013 |
'Fair' => [] |
| 1014 |
]; |
| 1015 |
|
| 1016 |
foreach ($combinations as $combo) { |
| 1017 |
$level_key = explode(' - ', $combo['level'])[0]; |
| 1018 |
if (isset($apca_groups[$level_key])) { |
| 1019 |
$apca_groups[$level_key][] = $combo; |
| 1020 |
} |
| 1021 |
} |
| 1022 |
|
| 1023 |
// Sort each group by absolute contrast value |
| 1024 |
foreach ($apca_groups as &$group) { |
| 1025 |
usort($group, function($a, $b) { |
| 1026 |
return abs($b['contrast']) <=> abs($a['contrast']); |
| 1027 |
}); |
| 1028 |
} |
| 1029 |
|
| 1030 |
$has_valid_combinations = !empty($apca_groups['Perfect']) || |
| 1031 |
!empty($apca_groups['Excellent']) || |
| 1032 |
!empty($apca_groups['Good']) || |
| 1033 |
!empty($apca_groups['Fair']); |
| 1034 |
} else { // both |
| 1035 |
// Group by combined level |
| 1036 |
$combined_groups = [ |
| 1037 |
'Perfect' => [], |
| 1038 |
'Excellent' => [], |
| 1039 |
'Good' => [], |
| 1040 |
'Fair' => [] |
| 1041 |
]; |
| 1042 |
|
| 1043 |
foreach ($combinations as $combo) { |
| 1044 |
if (isset($combined_groups[$combo['level']])) { |
| 1045 |
$combined_groups[$combo['level']][] = $combo; |
| 1046 |
} |
| 1047 |
} |
| 1048 |
|
| 1049 |
// Sort each group by WCAG contrast ratio |
| 1050 |
foreach ($combined_groups as &$group) { |
| 1051 |
usort($group, function($a, $b) { |
| 1052 |
return $b['wcag_contrast'] <=> $a['wcag_contrast']; |
| 1053 |
}); |
| 1054 |
} |
| 1055 |
|
| 1056 |
$has_valid_combinations = !empty($combined_groups['Perfect']) || |
| 1057 |
!empty($combined_groups['Excellent']) || |
| 1058 |
!empty($combined_groups['Good']) || |
| 1059 |
!empty($combined_groups['Fair']); |
| 1060 |
} |
| 1061 |
?> |
| 1062 |
<tr style="background-color: <?= htmlspecialchars(getCssColor($bg_color)) ?>;"> |
| 1063 |
<td> |
| 1064 |
<?php |
| 1065 |
// Use alpha-aware luminance for text color calculation |
| 1066 |
$bg_text_lum = getLuminance($bg['rgb'], $bg['alpha']); ?> |
| 1067 |
<span style="color: <?= $bg_text_lum > 0.5 ? '#000000' : '#FFFFFF' ?>"><?= htmlspecialchars(getCleanColorName($bg_color)) ?></span> |
| 1068 |
</td> |
| 1069 |
<?php if ($has_valid_combinations): ?> |
| 1070 |
<?php if ($current_method === 'wcag'): ?> |
| 1071 |
<?php foreach (['AAA', 'AA', 'AA Large'] as $level): ?> |
| 1072 |
<td><?php foreach ($wcag_groups[$level] as $combo): ?> |
| 1073 |
<div style="color: <?= htmlspecialchars(getCssColor($combo['color'])) ?>;"><?= htmlspecialchars($combo['color']) ?> (Ratio: <?= number_format($combo['contrast'], 2) ?>)</div> |
| 1074 |
<?php endforeach; ?></td> |
| 1075 |
<?php endforeach; ?> |
| 1076 |
<?php elseif ($current_method === 'apca'): ?> |
| 1077 |
<?php foreach (['Perfect', 'Excellent', 'Good', 'Fair'] as $level): ?> |
| 1078 |
<td><?php foreach ($apca_groups[$level] as $combo): ?> |
| 1079 |
<div style="color: <?= htmlspecialchars(getCssColor($combo['color'])) ?>;"><?= htmlspecialchars($combo['color']) ?> (Lc: <?= number_format($combo['contrast'], 1) ?>)</div> |
| 1080 |
<?php endforeach; ?></td> |
| 1081 |
<?php endforeach; ?> |
| 1082 |
<?php else: // both ?> |
| 1083 |
<?php foreach (['Perfect', 'Excellent', 'Good', 'Fair'] as $level): ?> |
| 1084 |
<td><?php foreach ($combined_groups[$level] as $combo): ?> |
| 1085 |
<div style="color: <?= htmlspecialchars(getCssColor($combo['color'])) ?>;"><?= htmlspecialchars($combo['color']) ?> (<?= number_format($combo['wcag_contrast'], 2) ?>) (Lc <?= number_format($combo['apca_contrast'], 1) ?>)</div> |
| 1086 |
<?php endforeach; ?></td> |
| 1087 |
<?php endforeach; ?> |
| 1088 |
<?php endif; ?> |
| 1089 |
<?php else: ?> |
| 1090 |
<td colspan="<?= $current_method === 'wcag' ? '3' : '4' ?>" style="text-align: center; color: <?= $bg_text_lum > 0.5 ? '#000000' : '#FFFFFF' ?>"> |
| 1091 |
<?php if ($current_method === 'wcag'): ?> |
| 1092 |
No valid color combinations found (all contrast ratios below 3.0) |
| 1093 |
<?php elseif ($current_method === 'apca'): ?> |
| 1094 |
No valid color combinations found (all APCA values below 45) |
| 1095 |
<?php else: ?> |
| 1096 |
No valid color combinations found (failed both WCAG and APCA thresholds) |
| 1097 |
<?php endif; ?> |
| 1098 |
</td> |
| 1099 |
<?php endif; ?> |
| 1100 |
</tr> |
| 1101 |
<?php endforeach; ?> |
| 1102 |
</tbody> |
| 1103 |
</table> |
| 1104 |
</div> |
| 1105 |
|
| 1106 |
<div class="table-wrapper"> |
| 1107 |
<h2>Complete Contrast Grid - Dark Mode (<?= $current_method === 'both' ? 'Both' : strtoupper($current_method) ?>)</h2> |
| 1108 |
<table class="checkered"> |
| 1109 |
<tr> |
| 1110 |
<th>BG \ FG</th> |
| 1111 |
<?php foreach ($fg_colors as $color => $data): ?> |
| 1112 |
<th><?= htmlspecialchars(getCleanColorName($color)) ?></th> |
| 1113 |
<?php endforeach; ?> |
| 1114 |
</tr> |
| 1115 |
<?php foreach ($bg_colors as $bg_color => $bg_data): ?> |
| 1116 |
<tr> |
| 1117 |
<th><?= htmlspecialchars(getCleanColorName($bg_color)) ?></th> |
| 1118 |
<?php foreach ($fg_colors as $fg_color => $fg_data): |
| 1119 |
if ($current_method === 'wcag') { |
| 1120 |
// Calculate luminance with alpha blending |
| 1121 |
$fg_lum = getLuminance($fg_data['rgb'], $fg_data['alpha'], $bg_data['rgb']); |
| 1122 |
$bg_lum = $bg_data['luminance']; |
| 1123 |
$contrast = getContrastRatio($bg_lum, $fg_lum); |
| 1124 |
$level = getWCAGLevel($contrast); |
| 1125 |
$display_value = number_format($contrast, 2); |
| 1126 |
} elseif ($current_method === 'apca') { |
| 1127 |
// APCA calculations |
| 1128 |
$contrast = getAPCAContrast($bg_data['rgb'], $fg_data['rgb'], $bg_data['alpha'], $fg_data['alpha']); |
| 1129 |
$level = getAPCALevel($contrast); |
| 1130 |
$display_value = number_format($contrast, 1); |
| 1131 |
} else { // both |
| 1132 |
// Calculate both WCAG and APCA |
| 1133 |
$fg_lum = getLuminance($fg_data['rgb'], $fg_data['alpha'], $bg_data['rgb']); |
| 1134 |
$bg_lum = $bg_data['luminance']; |
| 1135 |
$wcag_contrast = getContrastRatio($bg_lum, $fg_lum); |
| 1136 |
$wcag_level = getWCAGLevel($wcag_contrast); |
| 1137 |
|
| 1138 |
$apca_contrast = getAPCAContrast($bg_data['rgb'], $fg_data['rgb'], $bg_data['alpha'], $fg_data['alpha']); |
| 1139 |
$apca_level = getAPCALevel($apca_contrast); |
| 1140 |
|
| 1141 |
$combined_level = getCombinedLevel($wcag_contrast, $apca_contrast); |
| 1142 |
} |
| 1143 |
?> |
| 1144 |
<td style="background-color: <?= htmlspecialchars(getCssColor($bg_color)) ?>;"> |
| 1145 |
<!-- color value hover / alt text? --> |
| 1146 |
<div class="sample-text" style="color: <?= htmlspecialchars(getCssColor($fg_color)) ?>;">Sample |
| 1147 |
<div style="font-size: 0.8em;"> |
| 1148 |
<?php if ($current_method === 'wcag'): ?> |
| 1149 |
<?= $display_value ?><br><?= $level ?> |
| 1150 |
<?php elseif ($current_method === 'apca'): ?> |
| 1151 |
Lc <?= $display_value ?><br><?= explode(' - ', $level)[0] ?> |
| 1152 |
<?php else: // both ?> |
| 1153 |
(<?= number_format($wcag_contrast, 2) ?>) (Lc <?= number_format($apca_contrast, 1) ?>)<br><?= $combined_level ?> |
| 1154 |
<?php endif; ?> |
| 1155 |
</div> |
| 1156 |
</div> |
| 1157 |
</td> |
| 1158 |
<?php endforeach; ?> |
| 1159 |
</tr> |
| 1160 |
<?php endforeach; ?> |
| 1161 |
</table> |
| 1162 |
</div> |
| 1163 |
<?php endif; ?> |
| 1164 |
<div class="timer"><?php |
| 1165 |
$end = hrtime(true); |
| 1166 |
$nanoseconds = $end - $start; |
| 1167 |
// convert time to readable values |
| 1168 |
// Create an array of time units and their nanosecond conversions |
| 1169 |
$units = [ |
| 1170 |
'second' => 1e9, |
| 1171 |
'millisecond' => 1e6, |
| 1172 |
'microsecond' => 1e3, |
| 1173 |
'nanosecond' => 1 |
| 1174 |
]; |
| 1175 |
|
| 1176 |
// Find the most appropriate unit |
| 1177 |
$value = $nanoseconds; |
| 1178 |
$unit = 'nanosecond'; |
| 1179 |
|
| 1180 |
foreach ($units as $name => $divisor) { |
| 1181 |
if ($nanoseconds >= $divisor) { |
| 1182 |
$value = $nanoseconds / $divisor; |
| 1183 |
$unit = $name; |
| 1184 |
break; |
| 1185 |
} |
| 1186 |
} |
| 1187 |
|
| 1188 |
// Format the output with appropriate pluralization |
| 1189 |
$plural = $value != 1 ? 's' : ''; |
| 1190 |
$seconds = $nanoseconds / 1e9; // Always show seconds as well if using a different unit |
| 1191 |
|
| 1192 |
if ($unit !== 'second') { |
| 1193 |
echo sprintf("This code took %.6f seconds to execute, which is %.0f %s%s.", |
| 1194 |
$seconds, $value, $unit, $plural); |
| 1195 |
} else { |
| 1196 |
echo sprintf("This code took %.6f seconds to execute.", $seconds); |
| 1197 |
} |
| 1198 |
?></div> |
| 1199 |
<div class="footer"><?php echo getCopyrightYears(2024); ?> |
| 1200 |
</div> |
| 1201 |
<script src="https://contrast.jefftml.com/tablewidth.js"></script> |
| 1202 |
<script> |
| 1203 |
function toggleAdvancedMode() { |
| 1204 |
var advancedMode = document.getElementById('advanced_mode').checked; |
| 1205 |
var normalModeInput = document.getElementById('normal_mode_input'); |
| 1206 |
var advancedModeInputs = document.getElementById('advanced_mode_inputs'); |
| 1207 |
var colorsTextarea = document.getElementById('colors'); |
| 1208 |
|
| 1209 |
if (advancedMode) { |
| 1210 |
normalModeInput.style.display = 'none'; |
| 1211 |
advancedModeInputs.style.display = 'block'; |
| 1212 |
if (colorsTextarea) { |
| 1213 |
colorsTextarea.removeAttribute('required'); |
| 1214 |
} |
| 1215 |
} else { |
| 1216 |
normalModeInput.style.display = 'block'; |
| 1217 |
advancedModeInputs.style.display = 'none'; |
| 1218 |
if (colorsTextarea) { |
| 1219 |
colorsTextarea.setAttribute('required', 'required'); |
| 1220 |
} |
| 1221 |
} |
| 1222 |
} |
| 1223 |
|
| 1224 |
// Initialize on page load |
| 1225 |
if (document.readyState === 'loading') { |
| 1226 |
document.addEventListener('DOMContentLoaded', toggleAdvancedMode); |
| 1227 |
} else { |
| 1228 |
toggleAdvancedMode(); |
| 1229 |
} |
| 1230 |
|
| 1231 |
// Scroll to results if they exist (after form submission) |
| 1232 |
function scrollToResults() { |
| 1233 |
var resultsAnchor = document.getElementById('results'); |
| 1234 |
if (resultsAnchor) { |
| 1235 |
// Check if there are any results or error messages |
| 1236 |
var hasResults = document.querySelector('.error-message, .warning-message, .table-wrapper') !== null; |
| 1237 |
|
| 1238 |
if (hasResults) { |
| 1239 |
// Small delay to ensure page is fully rendered |
| 1240 |
setTimeout(function() { |
| 1241 |
resultsAnchor.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
| 1242 |
}, 100); |
| 1243 |
} |
| 1244 |
} |
| 1245 |
} |
| 1246 |
|
| 1247 |
// Run scroll check on page load |
| 1248 |
if (document.readyState === 'loading') { |
| 1249 |
document.addEventListener('DOMContentLoaded', scrollToResults); |
| 1250 |
} else { |
| 1251 |
scrollToResults(); |
| 1252 |
} |
| 1253 |
</script> |
| 1254 |
</div> |
| 1255 |
</main> |
| 1256 |
</body> |
| 1257 |
</html> |