src/ApplicationBundle/Resources/views/framework/_cp_tokens.html.twig line 1

Open in your IDE?
  1. {#
  2.   framework/_cp_tokens.html.twig
  3.   ══════════════════════════════════════════════════════════════════════════════
  4.   SINGLE SOURCE of cp-shell design tokens + shared component primitives.
  5.   Change the whole system's look HERE — every cp-shell module inherits it.
  6.   Per-module ACCENT: a module layout sets `--cp-accent` (+ hover/dim) on its
  7.   `.cp-shell` wrapper via inline style; every primitive below reads var(--cp-accent),
  8.   so one variable re-accents an entire module without touching this file.
  9.   Namespacing: component classes live under `.cp-scope` so they never leak into
  10.   legacy pages. `--cp-primary*` are kept as aliases of `--cp-accent*` for back-compat.
  11.   ══════════════════════════════════════════════════════════════════════════════
  12. #}
  13. <link rel="preconnect" href="https://fonts.googleapis.com">
  14. <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@600;700;800&family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
  15. <style>
  16. :root{
  17.   /* ── ACCENT (default; a module overrides --cp-accent on its .cp-shell) ── */
  18.   --cp-accent:#1f5673; --cp-accent-hover:#16465f; --cp-on-accent:#fff; --cp-accent-dim:rgba(31,86,115,.10);
  19.   /* back-compat aliases */
  20.   --cp-primary:var(--cp-accent); --cp-primary-hover:var(--cp-accent-hover); --cp-on-primary:var(--cp-on-accent);
  21.   /* ── neutrals / surfaces ── */
  22.   --cp-surface:#f7f9fb; --cp-surface-low:#eef3f6; --cp-surface-container:#e7eef2; --cp-surface-white:#fff;
  23.   --cp-text:#15242c; --cp-text-muted:#3f5560; --cp-text-faint:#76899a;
  24.   --cp-outline:#e2e9ee; --cp-outline-variant:#e2e9ee;
  25.   /* ── semantic ── */
  26.   --cp-success:#2e7d32; --cp-error:#ba1a1a; --cp-warn:#ef6c00; --cp-info:#2c6e9b;
  27.   --cp-success-dim:rgba(46,125,50,.10); --cp-error-dim:rgba(186,26,26,.10); --cp-warn-dim:rgba(239,108,0,.12);
  28.   /* ── shape / depth ── */
  29.   --cp-radius:8px; --cp-radius-lg:13px; --cp-radius-pill:99px;
  30.   --cp-shadow-card:0 1px 6px rgba(21,36,44,.06),0 0 0 1px rgba(31,86,115,.05);
  31.   --cp-shadow-pop:0 8px 28px rgba(21,36,44,.16);
  32.   /* ── type ── */
  33.   --cp-font-head:'Montserrat','DM Sans',system-ui,sans-serif;
  34.   --cp-font-body:'DM Sans',system-ui,sans-serif;
  35.   --cp-font-mono:'JetBrains Mono','Courier New',monospace;
  36. }
  37. /* ── scope base ── */
  38. .cp-scope{ font-family:var(--cp-font-body); color:var(--cp-text); }
  39. .cp-scope h1,.cp-scope h2,.cp-scope h3,.cp-scope h4{ font-family:var(--cp-font-head); color:var(--cp-text); letter-spacing:-.01em; }
  40. /* Accent colour is for LINKS. The :not() list has to exclude anchors that are really
  41.    buttons, or they get the module accent painted on top of their own background.
  42.    It already skipped the cp-native button classes but not the legacy ones, so
  43.    <a class="btn erp-btn erp-btn-primary">Save Proposal</a> on the proposal page
  44.    rendered #2b5fb8 text on its #854f00 background — a contrast ratio of 1.1:1
  45.    against the 4.5:1 AA floor, i.e. an all but invisible primary action. */
  46. .cp-scope a:not(.cp-btn):not(.cp-nav-link):not(.btn):not(.erp-btn){ color:var(--cp-accent); }
  47. /* buttons keep their own text colour regardless of the link rule above */
  48. .cp-scope a.cp-btn-primary,.cp-scope a.cp-btn-success,.cp-scope a.cp-btn-danger{ color:var(--cp-on-accent); }
  49. .cp-scope a.cp-btn-ghost{ color:var(--cp-text-muted); }
  50. /* ── Phase 4: converge button SHAPE, never button COLOUR ──────────────────────
  51.    Measured across 14 rendered pages, `.btn.btn-primary` came back in 5 colours
  52.    and 2 radii. The colours are DELIBERATE — amber #854f00, indigo #4f46e5 etc.
  53.    are module accents from the token system, and flattening them would delete a
  54.    feature. The radii are not: the legacy `style.css` carries
  55.    `.btn:not(.btn-link):not(.btn-circle){ border-radius:2px }` at (0,3,0), so a
  56.    page that does not load a newer button skin falls through to 2px while its
  57.    neighbours render 8px. Same class, same product, different shape.
  58.    This wins on specificity (0,4,0) and only inside a cp scope, so legacy pages
  59.    are untouched. It sets radius alone — colour, padding and font stay with
  60.    whatever owns them. */
  61. .cp-scope .btn:not(.btn-link):not(.btn-circle),
  62. .cp-content .btn:not(.btn-link):not(.btn-circle){ border-radius:var(--cp-radius); }
  63. /* ── card ── */
  64. .cp-card{ background:var(--cp-surface-white); border:1px solid var(--cp-outline); border-radius:var(--cp-radius-lg); box-shadow:var(--cp-shadow-card); padding:18px 20px; margin-bottom:16px; }
  65. .cp-card-t{ font-family:var(--cp-font-head); font-size:var(--cp-fs-base, 13px); font-weight:700; margin:0 0 14px; display:flex; align-items:center; gap:8px; }
  66. .cp-card-t .mi{ font-size:var(--cp-fs-lg, 16px); color:var(--cp-accent); }
  67. .cp-card-sub{ font-size:12px; color:var(--cp-text-faint); font-weight:400; margin-left:auto; }
  68. /* ── metric strip ── */
  69. .cp-metrics{ display:grid; grid-template-columns:repeat(auto-fill,minmax(172px,1fr)); gap:13px; margin-bottom:18px; }
  70. .cp-metric{ background:var(--cp-surface-white); border:1px solid var(--cp-outline); border-radius:var(--cp-radius-lg); box-shadow:var(--cp-shadow-card); padding:15px 17px; min-width:0; }
  71. /* A metric card may render as <a> when its figure is traceable to a list/report.
  72.    An anchor is inline and inherits link colour, which would collapse the card and
  73.    tint the number, so undo both. Shared here so every dashboard gets it for free. */
  74. a.cp-metric{ display:block; color:inherit; text-decoration:none; transition:border-color .15s, box-shadow .15s, transform .15s; }
  75. a.cp-metric:hover, a.cp-metric:focus{ color:inherit; text-decoration:none; border-color:var(--cp-accent, var(--cp-primary)); box-shadow:var(--cp-shadow-card-hover, var(--cp-shadow-card)); transform:translateY(-2px); }
  76. /* Where a metric has no list route to open, it jumps to the card on this page that
  77.    breaks it down. Flash that card so the landing is not silent. scroll-margin keeps
  78.    it clear of the sticky topbar. */
  79. .cp-card[id]{ scroll-margin-top:78px; }
  80. .cp-card:target{ border-color:var(--cp-accent, var(--cp-primary)); box-shadow:0 0 0 3px var(--cp-accent-dim, rgba(31,86,115,.14)); }
  81. .cp-metric .mi{ font-size:18px; color:var(--cp-accent); }
  82. .cp-metric-val{ font-family:var(--cp-font-head); font-size:22px; font-weight:800; letter-spacing:-.02em; margin-top:6px; line-height:1.15; overflow-wrap:anywhere; font-variant-numeric:tabular-nums; }
  83. .cp-metric-lbl{ font-family:var(--cp-font-mono); font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:.05em; color:var(--cp-text-faint); margin-top:2px; }
  84. /* ── table ── */
  85. .cp-table{ width:100%; border-collapse:collapse; font-size:var(--cp-fs-sm, 12.5px); }
  86. .cp-table th{ font-family:var(--cp-font-mono); font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:.05em; color:var(--cp-text-faint); text-align:left; padding:9px 11px; border-bottom:1px solid var(--cp-outline); background:var(--cp-surface-low); white-space:nowrap; }
  87. .cp-table td{ padding:10px 11px; border-bottom:1px solid var(--cp-surface-container); color:var(--cp-text-muted); vertical-align:middle; }
  88. .cp-table tbody tr:hover td{ background:var(--cp-accent-dim); }
  89. .cp-table td.num,.cp-table th.num{ text-align:right; font-variant-numeric:tabular-nums; }
  90. .cp-table td.strong{ color:var(--cp-text); font-weight:600; }
  91. /* ── definition grid (document detail) ── */
  92. .cp-dl{ display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:2px 22px; }
  93. .cp-dl .cp-dl-i{ margin-bottom:10px; }
  94. .cp-dl dt{ font-family:var(--cp-font-mono); font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:.05em; color:var(--cp-text-faint); margin-bottom:2px; }
  95. .cp-dl dd{ font-size:var(--cp-fs-base, 13px); color:var(--cp-text); margin:0; font-weight:500; }
  96. /* ── buttons ── */
  97. .cp-btn{ display:inline-flex; align-items:center; gap:6px; padding:8px 15px; border-radius:var(--cp-radius); font-family:var(--cp-font-body); font-size:var(--cp-fs-sm, 12.5px); font-weight:600; border:1px solid transparent; cursor:pointer; text-decoration:none; transition:background .12s,border-color .12s; line-height:1.2; }
  98. .cp-btn .mi{ font-size:var(--cp-fs-md, 15px); }
  99. .cp-btn-primary{ background:var(--cp-accent); color:var(--cp-on-accent); }
  100. .cp-btn-primary:hover{ background:var(--cp-accent-hover); color:var(--cp-on-accent); text-decoration:none; }
  101. .cp-btn-ghost{ background:var(--cp-surface-white); color:var(--cp-text-muted); border-color:var(--cp-outline); }
  102. .cp-btn-ghost:hover{ background:var(--cp-surface-low); color:var(--cp-text); text-decoration:none; }
  103. .cp-btn-success{ background:var(--cp-success); color:var(--cp-on-accent, #fff); }
  104. .cp-btn-danger{ background:var(--cp-error); color:var(--cp-on-accent, #fff); }
  105. .cp-btn-sm{ padding:5px 10px; font-size:12px; }
  106. /* ── pill / status ── */
  107. .cp-pill{ display:inline-flex; align-items:center; gap:5px; font-family:var(--cp-font-mono); font-size:11px; font-weight:600; letter-spacing:.04em; text-transform:uppercase; padding:3px 10px; border-radius:var(--cp-radius-pill); }
  108. .cp-pill-accent{ background:var(--cp-accent-dim); color:var(--cp-accent); }
  109. .cp-pill-success{ background:var(--cp-success-dim); color:var(--cp-success); }
  110. .cp-pill-warn{ background:var(--cp-warn-dim); color:var(--cp-warn); }
  111. .cp-pill-danger{ background:var(--cp-error-dim); color:var(--cp-error); }
  112. .cp-pill-muted{ background:var(--cp-surface-container); color:var(--cp-text-muted); }
  113. /* ── filter bar / fields ── */
  114. .cp-filters{ display:flex; flex-wrap:wrap; gap:10px; align-items:flex-end; margin-bottom:14px; }
  115. .cp-field{ display:flex; flex-direction:column; gap:4px; }
  116. .cp-field label{ font-family:var(--cp-font-mono); font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:.05em; color:var(--cp-text-faint); }
  117. .cp-field input,.cp-field select{ padding:7px 10px; border:1px solid var(--cp-outline); border-radius:var(--cp-radius); font-size:var(--cp-fs-sm, 12.5px); background:var(--cp-surface-white); color:var(--cp-text); font-family:var(--cp-font-body); min-width:150px; }
  118. /* ── empty / note ── */
  119. .cp-empty{ padding:34px 20px; text-align:center; color:var(--cp-text-faint); font-size:var(--cp-fs-base, 13px); }
  120. .cp-empty .mi{ font-size:34px; opacity:.5; display:block; margin-bottom:8px; }
  121. .cp-empty-act{ margin-top:10px; }
  122. /* Loading + error. A list had no shared way to say "working" or "this failed", so pages
  123.    either showed nothing or invented their own — one more vocabulary each time. Same
  124.    footprint as .cp-empty so a list can swap between the three states without reflowing. */
  125. .cp-loading{ padding:34px 20px; text-align:center; color:var(--cp-text-faint); font-size:var(--cp-fs-base, 13px); }
  126. .cp-loading::before{ content:''; display:inline-block; width:18px; height:18px; margin-bottom:8px;
  127.   border:2px solid var(--cp-outline); border-top-color:var(--cp-accent); border-radius:50%;
  128.   animation:cp-spin .7s linear infinite; }
  129. @keyframes cp-spin{ to{ transform:rotate(360deg); } }
  130. @media (prefers-reduced-motion: reduce){ .cp-loading::before{ animation:none; } }
  131. .cp-error{ padding:14px 16px; border-radius:var(--cp-radius); font-size:var(--cp-fs-base, 13px); line-height:1.5;
  132.   background:var(--cp-error-dim); color:var(--cp-error); border:1px solid var(--cp-error); }
  133. .cp-error .mi{ vertical-align:-4px; margin-right:6px; }
  134. /* Form controls. 138 form-control uses on the proposal page alone had no cp equivalent, so
  135.    every form reached for Bootstrap 3 defaults. 32px min-height is the density floor from the
  136.    contract — the audit found controls rendering at 30px and smaller. */
  137. .cp-field{ margin-bottom:12px; }
  138. .cp-label{ display:block; font-size:12px; font-weight:600; color:var(--cp-text-muted); margin-bottom:4px; }
  139. .cp-label .req{ color:var(--cp-error); margin-left:2px; }
  140. .cp-input,.cp-select,.cp-textarea{ width:100%; min-height:32px; padding:6px 10px; font-family:inherit;
  141.   font-size:var(--cp-fs-base, 13px); color:var(--cp-text); background:var(--cp-surface-white);
  142.   border:1px solid var(--cp-outline); border-radius:var(--cp-radius); }
  143. .cp-input:focus,.cp-select:focus,.cp-textarea:focus{ outline:none; border-color:var(--cp-accent);
  144.   box-shadow:0 0 0 3px var(--cp-accent-dim); }
  145. .cp-input[disabled],.cp-select[disabled]{ background:var(--cp-surface-low); color:var(--cp-text-faint); }
  146. .cp-textarea{ min-height:74px; resize:vertical; }
  147. .cp-hint{ font-size:var(--cp-fs-xs, 11.5px); color:var(--cp-text-faint); margin-top:3px; }
  148. .cp-field.is-invalid .cp-input,.cp-field.is-invalid .cp-select{ border-color:var(--cp-error); }
  149. .cp-field.is-invalid .cp-hint{ color:var(--cp-error); }
  150. .cp-note{ font-size:12px; color:var(--cp-text-faint); line-height:1.5; }
  151. </style>