Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

264 řádky
13KB

  1. <title>PostcardMojo Logo Files</title>
  2. <meta name="claude:viewport-fit-sides" content="cover">
  3. <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fredoka:wght@500;600&family=Figtree:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap">
  4. <style>
  5. /* Layout: brand header, sticky-free control bar, then one comparison row per logo: PNG panel | JPG panel, stacking on narrow screens */
  6. :root {
  7. --bg: #f6f5f1;
  8. --surface: #ffffff;
  9. --fg: #1a2a3a;
  10. --muted: #5f6b76;
  11. --line: #e2dfd6;
  12. --navy: #1b3a57;
  13. --gold: #b88a1c;
  14. --good-bg: #e6f2ea;
  15. --good-fg: #1f6b3a;
  16. --warn-bg: #fdeeea;
  17. --warn-fg: #a3341c;
  18. --swatch-light: #ffffff;
  19. --swatch-dark: #172a3d;
  20. --check-a: #ffffff;
  21. --check-b: #e8e6e0;
  22. --focus: #b88a1c;
  23. --font-display: "Fredoka", "Nunito", system-ui, sans-serif;
  24. --font-body: "Figtree", system-ui, -apple-system, "Segoe UI", sans-serif;
  25. --font-mono: "JetBrains Mono", ui-monospace, Consolas, monospace;
  26. }
  27. @media (prefers-color-scheme: dark) {
  28. :root:not([data-theme="light"]) {
  29. --bg: #11161c; --surface: #19212a; --fg: #e8ecef; --muted: #9aa6b1; --line: #2a3542;
  30. --navy: #9cc0e2; --gold: #e2bb55; --good-bg: #17301f; --good-fg: #8fd3a5;
  31. --warn-bg: #3a1f19; --warn-fg: #f4a48f;
  32. --check-a: #2a3440; --check-b: #222b35; --focus: #e2bb55; color-scheme: dark;
  33. }
  34. }
  35. :root[data-theme="dark"] {
  36. --bg: #11161c; --surface: #19212a; --fg: #e8ecef; --muted: #9aa6b1; --line: #2a3542;
  37. --navy: #9cc0e2; --gold: #e2bb55; --good-bg: #17301f; --good-fg: #8fd3a5;
  38. --warn-bg: #3a1f19; --warn-fg: #f4a48f;
  39. --check-a: #2a3440; --check-b: #222b35; --focus: #e2bb55; color-scheme: dark;
  40. }
  41. body {
  42. background: var(--bg); color: var(--fg); font-family: var(--font-body); font-size: 15px; line-height: 1.5;
  43. padding-left: max(16px, env(safe-area-inset-left, 0px));
  44. padding-right: max(16px, env(safe-area-inset-right, 0px));
  45. padding-block: 40px 64px;
  46. }
  47. .wrap { max-width: 1180px; margin: 0 auto; display: grid; gap: 32px; }
  48. header { display: grid; gap: 8px; }
  49. h1 { font-family: var(--font-display); font-weight: 600; font-size: 2rem; line-height: 1.15; margin: 0; text-wrap: balance; color: var(--navy); }
  50. h1 span { color: var(--gold); }
  51. .lede { margin: 0; color: var(--muted); max-width: 66ch; }
  52. .totals { display: flex; flex-wrap: wrap; gap: 8px 20px; font-size: 0.875rem; color: var(--muted); }
  53. .totals b { color: var(--fg); font-family: var(--font-mono); font-weight: 500; font-variant-numeric: tabular-nums; }
  54. .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 24px; }
  55. .ctl { display: flex; align-items: center; gap: 10px; }
  56. .ctl .label { font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); font-weight: 600; }
  57. .seg { display: inline-flex; border: 1px solid var(--line); border-radius: 999px; padding: 3px; background: var(--surface); }
  58. .seg button {
  59. font: inherit; font-size: 0.875rem; font-weight: 500; border: 0; background: transparent; color: var(--muted);
  60. padding: 6px 14px; border-radius: 999px; cursor: pointer;
  61. }
  62. .seg button[aria-pressed="true"] { background: var(--navy); color: var(--bg); }
  63. button:focus-visible, .panel .view:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
  64. section { display: grid; gap: 14px; }
  65. h2 { font-family: var(--font-display); font-weight: 500; font-size: 1.25rem; margin: 0; }
  66. h2 small { font-family: var(--font-body); font-size: 0.875rem; color: var(--muted); font-weight: 400; margin-left: 6px; }
  67. .list { display: grid; gap: 16px; }
  68. .list.icons { grid-template-columns: repeat(auto-fill, minmax(min(100%, 520px), 1fr)); }
  69. .item { background: var(--surface); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; display: grid; min-width: 0; }
  70. .item-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; padding: 12px 14px; border-bottom: 1px solid var(--line); }
  71. .name { font-family: var(--font-mono); font-size: 0.875rem; font-weight: 500; overflow-wrap: anywhere; }
  72. .dims { font-family: var(--font-mono); font-size: 0.8125rem; color: var(--muted); font-variant-numeric: tabular-nums; }
  73. .pill { font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; padding: 2px 8px; border-radius: 999px; }
  74. .pill.warn { background: var(--warn-bg); color: var(--warn-fg); }
  75. .pill.good { background: var(--good-bg); color: var(--good-fg); }
  76. .compare { display: grid; grid-template-columns: 1fr 1fr; }
  77. .item.wide .compare { grid-template-columns: 1fr; }
  78. @media (max-width: 640px) { .compare { grid-template-columns: 1fr; } }
  79. .panel { display: grid; grid-template-rows: auto 1fr; min-width: 0; }
  80. .panel + .panel { border-left: 1px solid var(--line); }
  81. .item.wide .panel + .panel { border-left: 0; border-top: 1px solid var(--line); }
  82. @media (max-width: 640px) { .panel + .panel { border-left: 0; border-top: 1px solid var(--line); } }
  83. .panel-label { display: flex; justify-content: space-between; gap: 8px; padding: 8px 14px; font-size: 0.8125rem; color: var(--muted); }
  84. .panel-label .fmt { font-weight: 600; color: var(--fg); letter-spacing: 0.04em; }
  85. .panel-label .kb { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
  86. .view { overflow: auto; padding: 24px 20px; display: grid; place-items: center; min-height: 140px; }
  87. .view img { display: block; max-width: none; height: auto; }
  88. body[data-zoom="1"] .view img { max-width: 100%; }
  89. body:not([data-zoom="1"]) .view { place-items: start; }
  90. body:not([data-zoom="1"]) .view img { image-rendering: pixelated; }
  91. body[data-bg="light"] .view { background: var(--swatch-light); }
  92. body[data-bg="dark"] .view { background: var(--swatch-dark); }
  93. body[data-bg="checker"] .view {
  94. background-color: var(--check-a);
  95. background-image: linear-gradient(45deg, var(--check-b) 25%, transparent 25%, transparent 75%, var(--check-b) 75%),
  96. linear-gradient(45deg, var(--check-b) 25%, transparent 25%, transparent 75%, var(--check-b) 75%);
  97. background-size: 16px 16px; background-position: 0 0, 8px 8px;
  98. }
  99. footer { color: var(--muted); font-size: 0.8125rem; border-top: 1px solid var(--line); padding-top: 16px; display: grid; gap: 6px; }
  100. footer p { margin: 0; max-width: 80ch; }
  101. code { font-family: var(--font-mono); font-size: 0.92em; }
  102. </style>
  103. <div class="wrap">
  104. <header>
  105. <h1>postcard<span>mojo</span> logo files</h1>
  106. <p class="lede">Each logo as a transparent PNG (left) and a white-background JPG (right). Zoom in to look for compression artifacts. Switch to a dark background to see where the JPG's white box shows.</p>
  107. <div class="totals" id="totals"></div>
  108. </header>
  109. <div class="controls">
  110. <div class="ctl" role="group" aria-label="Preview background">
  111. <span class="label">Background</span>
  112. <div class="seg" id="bg-seg">
  113. <button type="button" id="bg-light" data-bg="light" aria-pressed="true">Light</button>
  114. <button type="button" id="bg-dark" data-bg="dark" aria-pressed="false">Dark</button>
  115. <button type="button" id="bg-checker" data-bg="checker" aria-pressed="false">Transparent</button>
  116. </div>
  117. </div>
  118. <div class="ctl" role="group" aria-label="Zoom">
  119. <span class="label">Zoom</span>
  120. <div class="seg" id="zoom-seg">
  121. <button type="button" id="zoom-1" data-zoom="1" aria-pressed="true">Fit</button>
  122. <button type="button" id="zoom-2" data-zoom="2" aria-pressed="false">2×</button>
  123. <button type="button" id="zoom-4" data-zoom="4" aria-pressed="false">4×</button>
  124. </div>
  125. </div>
  126. </div>
  127. <section>
  128. <h2>Full logo <small>card icon with wordmark</small></h2>
  129. <div class="list" id="lockups"></div>
  130. </section>
  131. <section>
  132. <h2>Icon <small>postcard mark only</small></h2>
  133. <div class="list icons" id="icons"></div>
  134. </section>
  135. <footer>
  136. <p>PNGs are in <code>images/logos/png/</code> and JPGs in <code>images/logos/jpg/</code>. Sizes are the actual pixel size of each file. The names come from the labels on the original sheet, which don't match the real sizes.</p>
  137. <p>At 2× and 4×, scrolling one panel scrolls its partner too, so both show the same spot.</p>
  138. </footer>
  139. </div>
  140. <script>
  141. (function () {
  142. // w, h = pixels; png, jpg = file size in KB
  143. var lockups = [
  144. { file: 'logo_full_large', w: 796, h: 220, png: 216.4, jpg: 26.7, wide: true },
  145. { file: 'logo_full_medium_c', w: 411, h: 123, png: 68.8, jpg: 12.9 },
  146. { file: 'logo_full_medium_a', w: 285, h: 88, png: 38.0, jpg: 7.2 },
  147. { file: 'logo_full_small_b', w: 217, h: 73, png: 24.0, jpg: 4.8 },
  148. { file: 'logo_full_small_a', w: 187, h: 63, png: 18.7, jpg: 3.9 },
  149. { file: 'logo_full_medium_b_misprint', w: 535, h: 129, png: 96.1, jpg: 17.7, misprint: true }
  150. ];
  151. var icons = [
  152. { file: 'icon_256', w: 169, h: 129, png: 41.2, jpg: 6.7 },
  153. { file: 'icon_64', w: 143, h: 85, png: 21.5, jpg: 4.0 },
  154. { file: 'icon_24', w: 95, h: 81, png: 14.1, jpg: 3.8 },
  155. { file: 'icon_small_8', w: 90, h: 72, png: 13.9, jpg: 3.0 }
  156. ];
  157. function el(tag, cls, text) {
  158. var e = document.createElement(tag);
  159. if (cls) e.className = cls;
  160. if (text != null) e.textContent = text;
  161. return e;
  162. }
  163. function panel(fmt, src, kb, item) {
  164. var p = el('div', 'panel');
  165. var label = el('div', 'panel-label');
  166. label.append(el('span', 'fmt', fmt), el('span', 'kb', kb.toFixed(1) + ' KB'));
  167. var view = el('div', 'view');
  168. view.tabIndex = 0;
  169. view.setAttribute('aria-label', fmt + ' preview of ' + item.file);
  170. var img = el('img');
  171. img.src = src; img.alt = item.file + ' as ' + fmt;
  172. img.width = item.w; img.height = item.h;
  173. img.dataset.w = item.w;
  174. view.append(img);
  175. p.append(label, view);
  176. return p;
  177. }
  178. function render(list, target) {
  179. list.forEach(function (item) {
  180. var card = el('article', 'item' + (item.wide ? ' wide' : ''));
  181. var head = el('div', 'item-head');
  182. head.append(el('span', 'name', item.file), el('span', 'dims', item.w + ' × ' + item.h));
  183. var saved = Math.round((1 - item.jpg / item.png) * 100);
  184. head.append(el('span', 'pill good', 'JPG ' + saved + '% smaller'));
  185. if (item.misprint) head.append(el('span', 'pill warn', 'Do not use: reads "pomostcard"'));
  186. var cmp = el('div', 'compare');
  187. var a = panel('PNG', 'images/logos/png/' + item.file + '.png', item.png, item);
  188. var b = panel('JPG', 'images/logos/jpg/' + item.file + '.jpg', item.jpg, item);
  189. cmp.append(a, b);
  190. linkScroll(a.querySelector('.view'), b.querySelector('.view'));
  191. card.append(head, cmp);
  192. target.append(card);
  193. });
  194. }
  195. function linkScroll(x, y) {
  196. var lock = false;
  197. function sync(from, to) {
  198. from.addEventListener('scroll', function () {
  199. if (lock) return;
  200. lock = true;
  201. to.scrollLeft = from.scrollLeft; to.scrollTop = from.scrollTop;
  202. requestAnimationFrame(function () { lock = false; });
  203. });
  204. }
  205. sync(x, y); sync(y, x);
  206. }
  207. render(lockups, document.getElementById('lockups'));
  208. render(icons, document.getElementById('icons'));
  209. var all = lockups.concat(icons);
  210. var pngTotal = all.reduce(function (s, i) { return s + i.png; }, 0);
  211. var jpgTotal = all.reduce(function (s, i) { return s + i.jpg; }, 0);
  212. var totals = document.getElementById('totals');
  213. totals.innerHTML = '';
  214. [['All PNGs', pngTotal.toFixed(0) + ' KB'], ['All JPGs', jpgTotal.toFixed(0) + ' KB'],
  215. ['Saved', Math.round((1 - jpgTotal / pngTotal) * 100) + '%']].forEach(function (t) {
  216. var s = el('span', null, t[0] + ' ');
  217. s.append(el('b', null, t[1]));
  218. totals.append(s);
  219. });
  220. function setBg(bg) {
  221. document.body.setAttribute('data-bg', bg);
  222. document.querySelectorAll('#bg-seg button').forEach(function (b) { b.setAttribute('aria-pressed', String(b.dataset.bg === bg)); });
  223. try { localStorage.setItem('logo-bg', bg); } catch (e) {}
  224. }
  225. function setZoom(z) {
  226. document.body.setAttribute('data-zoom', z);
  227. document.querySelectorAll('#zoom-seg button').forEach(function (b) { b.setAttribute('aria-pressed', String(b.dataset.zoom === z)); });
  228. document.querySelectorAll('.view img').forEach(function (img) { img.style.width = (img.dataset.w * Number(z)) + 'px'; });
  229. if (z === '1') document.querySelectorAll('.view img').forEach(function (img) { img.style.width = ''; });
  230. try { localStorage.setItem('logo-zoom', z); } catch (e) {}
  231. }
  232. document.querySelectorAll('#bg-seg button').forEach(function (b) { b.addEventListener('click', function () { setBg(b.dataset.bg); }); });
  233. document.querySelectorAll('#zoom-seg button').forEach(function (b) { b.addEventListener('click', function () { setZoom(b.dataset.zoom); }); });
  234. var savedBg = 'light', savedZoom = '1';
  235. try { savedBg = localStorage.getItem('logo-bg') || 'light'; savedZoom = localStorage.getItem('logo-zoom') || '1'; } catch (e) {}
  236. setBg(savedBg);
  237. setZoom(savedZoom);
  238. })();
  239. </script>

Powered by TurnKey Linux.