input.css 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. @tailwind base;
  2. @tailwind components;
  3. @tailwind utilities;
  4. @layer base {
  5. /* Hide native spinner buttons on every number input app-wide. The
  6. team prefers keyboard typing over the tiny, inconsistent UA
  7. controls. */
  8. input[type="number"]::-webkit-outer-spin-button,
  9. input[type="number"]::-webkit-inner-spin-button {
  10. -webkit-appearance: none;
  11. margin: 0;
  12. }
  13. input[type="number"] {
  14. -moz-appearance: textfield;
  15. appearance: textfield;
  16. }
  17. }
  18. @layer utilities {
  19. /* Phase 13: the Focus filter temporarily hides entire sw columns when the
  20. focused worker has no assignment on any visible row. Separate from the
  21. .hidden set driven by the Columns dropdown so clearing Focus restores
  22. the user's manual column picks. */
  23. .focus-auto-hidden {
  24. display: none;
  25. }
  26. }
  27. @layer components {
  28. /* Phase 15: big-screen / beamer presentation scope. Applied via
  29. views/sprints/present.php on the root <main>. Tightens typography
  30. and cell padding so the task table fits the viewport without
  31. horizontal scroll at 1920×1080; hides drag handles and per-row
  32. delete buttons (not meaningful during a group discussion). */
  33. .beamer-root table {
  34. table-layout: fixed;
  35. font-size: clamp(0.75rem, 0.95vw, 1.05rem);
  36. }
  37. .beamer-root td,
  38. .beamer-root th {
  39. padding: 0.25rem 0.35rem;
  40. }
  41. .beamer-root .handle,
  42. .beamer-root [data-delete-task],
  43. .beamer-root [data-task-menu-trigger] {
  44. display: none;
  45. }
  46. /* JS-driven toggle (sprint-planner.js): when the rendered task table
  47. is wider than the viewport, rotate worker-column headers to save
  48. horizontal space. Leaves non-worker headers (Task / Owner / Prio
  49. / Tot) untouched. */
  50. .beamer-vertical-headers thead th[data-sort-col^="sw-"] {
  51. writing-mode: vertical-rl;
  52. transform: rotate(180deg);
  53. padding: 0.5rem 0.25rem;
  54. }
  55. /* Phase 18: per-cell task-assignment status. The colour class lives
  56. on the <td> (so JS / filters can read data-status off the cell),
  57. but the visible tint is applied to the day input / read-only span
  58. inside — colouring just the <td> made the cell padding stripe a
  59. different colour from the field, which read as visual noise. The
  60. four .assign-status-* class names are interpolated server-side
  61. ("assign-status-<?= $st ?>"), so they're held in
  62. tailwind.config.js's safelist; otherwise the JIT prunes them. */
  63. .assign-status-zugewiesen { /* default: no tint */ }
  64. .assign-status-gestartet input[data-assign],
  65. .assign-status-gestartet > span.font-mono { background-color: theme('colors.yellow.200'); }
  66. .assign-status-abgeschlossen input[data-assign],
  67. .assign-status-abgeschlossen > span.font-mono { background-color: theme('colors.green.200'); }
  68. .assign-status-abgebrochen input[data-assign],
  69. .assign-status-abgebrochen > span.font-mono { background-color: theme('colors.red.200'); }
  70. .dark .assign-status-gestartet input[data-assign],
  71. .dark .assign-status-gestartet > span.font-mono { background-color: theme('colors.yellow.700'); }
  72. .dark .assign-status-abgeschlossen input[data-assign],
  73. .dark .assign-status-abgeschlossen > span.font-mono { background-color: theme('colors.green.700'); }
  74. .dark .assign-status-abgebrochen input[data-assign],
  75. .dark .assign-status-abgebrochen > span.font-mono { background-color: theme('colors.red.700'); }
  76. /* Cell popover — replaces the per-cell chevron status select.
  77. Body-attached, position: absolute (set in JS), shown to the
  78. right of the bound input/cell. Carries a 0..N slider (max
  79. configured via /settings → assignment_slider_max) and a
  80. column of status pills with coloured bullets. */
  81. .cell-popover {
  82. position: absolute;
  83. z-index: 50;
  84. min-width: 14rem;
  85. padding: 0.75rem;
  86. border-radius: 0.5rem;
  87. border: 1px solid theme('colors.slate.200');
  88. background-color: theme('colors.white');
  89. box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  90. }
  91. .dark .cell-popover {
  92. border-color: theme('colors.slate.700');
  93. background-color: theme('colors.slate.800');
  94. }
  95. .cell-popover-grid {
  96. display: grid;
  97. grid-template-columns: auto 1fr;
  98. gap: 1rem;
  99. align-items: center;
  100. }
  101. .cell-popover-slider {
  102. display: flex;
  103. flex-direction: column;
  104. align-items: center;
  105. gap: 0.4rem;
  106. min-width: 2.5rem;
  107. }
  108. /* Vertical slider — `writing-mode + direction: rtl` covers modern
  109. browsers (low at the bottom, high at the top); the WebKit
  110. `slider-vertical` appearance and the Firefox `orient="vertical"`
  111. attribute (set in JS) are belt-and-braces for older engines. */
  112. .cell-popover-slider input[type="range"] {
  113. writing-mode: vertical-lr;
  114. direction: rtl;
  115. -webkit-appearance: slider-vertical;
  116. appearance: slider-vertical;
  117. width: 1.5rem;
  118. height: 8rem;
  119. accent-color: theme('colors.slate.600');
  120. cursor: pointer;
  121. }
  122. .dark .cell-popover-slider input[type="range"] {
  123. accent-color: theme('colors.slate.400');
  124. }
  125. .cell-popover-value {
  126. text-align: center;
  127. font-family: theme('fontFamily.mono');
  128. font-size: 0.875rem;
  129. color: theme('colors.slate.700');
  130. }
  131. .dark .cell-popover-value {
  132. color: theme('colors.slate.200');
  133. }
  134. .cell-popover-status {
  135. display: flex;
  136. flex-direction: column;
  137. gap: 0.25rem;
  138. min-width: 9rem;
  139. }
  140. .cell-popover-status-btn {
  141. display: flex;
  142. align-items: center;
  143. gap: 0.5rem;
  144. padding: 0.25rem 0.5rem;
  145. border-radius: 4px;
  146. text-align: left;
  147. font-size: 0.875rem;
  148. color: theme('colors.slate.700');
  149. background-color: transparent;
  150. border: 1px solid transparent;
  151. cursor: pointer;
  152. }
  153. .dark .cell-popover-status-btn {
  154. color: theme('colors.slate.200');
  155. }
  156. .cell-popover-status-btn:hover {
  157. background-color: theme('colors.slate.100');
  158. }
  159. .dark .cell-popover-status-btn:hover {
  160. background-color: theme('colors.slate.700');
  161. }
  162. .cell-popover-status-btn.cell-popover-active {
  163. border-color: theme('colors.slate.300');
  164. background-color: theme('colors.slate.50');
  165. }
  166. .dark .cell-popover-status-btn.cell-popover-active {
  167. border-color: theme('colors.slate.600');
  168. background-color: theme('colors.slate.700');
  169. }
  170. .cell-popover-bullet {
  171. display: inline-block;
  172. width: 0.75rem;
  173. height: 0.75rem;
  174. border-radius: 9999px;
  175. flex-shrink: 0;
  176. }
  177. .cell-popover-bullet.bullet-zugewiesen {
  178. border: 1px solid theme('colors.slate.300');
  179. }
  180. .dark .cell-popover-bullet.bullet-zugewiesen {
  181. border-color: theme('colors.slate.600');
  182. }
  183. .cell-popover-bullet.bullet-gestartet { background-color: theme('colors.yellow.300'); }
  184. .cell-popover-bullet.bullet-abgeschlossen { background-color: theme('colors.green.300'); }
  185. .cell-popover-bullet.bullet-abgebrochen { background-color: theme('colors.red.300'); }
  186. .dark .cell-popover-bullet.bullet-gestartet { background-color: theme('colors.yellow.500'); }
  187. .dark .cell-popover-bullet.bullet-abgeschlossen { background-color: theme('colors.green.500'); }
  188. .dark .cell-popover-bullet.bullet-abgebrochen { background-color: theme('colors.red.500'); }
  189. /* Phase 22: per-task hamburger menu, details modal, click-to-pickup
  190. reorder indicator, description popover. All single body-attached
  191. nodes; the JS positions them with absolute coordinates. */
  192. .task-menu {
  193. position: absolute;
  194. z-index: 60;
  195. min-width: 12rem;
  196. padding: 0.25rem;
  197. border-radius: 0.5rem;
  198. border: 1px solid theme('colors.slate.200');
  199. background-color: theme('colors.white');
  200. box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.12), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  201. }
  202. .dark .task-menu {
  203. border-color: theme('colors.slate.700');
  204. background-color: theme('colors.slate.800');
  205. }
  206. .task-menu-item,
  207. .task-menu-sub-item {
  208. display: flex;
  209. width: 100%;
  210. align-items: center;
  211. justify-content: space-between;
  212. gap: 0.5rem;
  213. padding: 0.4rem 0.6rem;
  214. border-radius: 4px;
  215. text-align: left;
  216. font-size: 0.875rem;
  217. color: theme('colors.slate.700');
  218. background-color: transparent;
  219. cursor: pointer;
  220. }
  221. .dark .task-menu-item,
  222. .dark .task-menu-sub-item {
  223. color: theme('colors.slate.200');
  224. }
  225. .task-menu-item:hover,
  226. .task-menu-sub-item:hover {
  227. background-color: theme('colors.slate.100');
  228. }
  229. .dark .task-menu-item:hover,
  230. .dark .task-menu-sub-item:hover {
  231. background-color: theme('colors.slate.700');
  232. }
  233. .task-menu-item-danger {
  234. color: theme('colors.red.600');
  235. }
  236. .dark .task-menu-item-danger {
  237. color: theme('colors.red.400');
  238. }
  239. .task-menu-subwrap {
  240. position: relative;
  241. }
  242. .task-menu-sub {
  243. margin-top: 0.15rem;
  244. margin-left: 0.5rem;
  245. padding: 0.25rem;
  246. border-radius: 0.4rem;
  247. border: 1px solid theme('colors.slate.200');
  248. background-color: theme('colors.slate.50');
  249. max-height: 14rem;
  250. overflow-y: auto;
  251. }
  252. .dark .task-menu-sub {
  253. border-color: theme('colors.slate.700');
  254. background-color: theme('colors.slate.700');
  255. }
  256. .task-menu-sub-empty {
  257. padding: 0.4rem 0.6rem;
  258. font-size: 0.8rem;
  259. font-style: italic;
  260. color: theme('colors.slate.500');
  261. }
  262. .dark .task-menu-sub-empty {
  263. color: theme('colors.slate.400');
  264. }
  265. .task-modal-overlay {
  266. position: fixed;
  267. inset: 0;
  268. z-index: 70;
  269. display: flex;
  270. align-items: center;
  271. justify-content: center;
  272. padding: 1rem;
  273. background-color: rgb(15 23 42 / 0.45);
  274. }
  275. .task-modal-panel {
  276. width: 100%;
  277. max-width: 32rem;
  278. border-radius: 0.5rem;
  279. background-color: theme('colors.white');
  280. border: 1px solid theme('colors.slate.200');
  281. box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.3);
  282. display: flex;
  283. flex-direction: column;
  284. overflow: hidden;
  285. }
  286. .dark .task-modal-panel {
  287. background-color: theme('colors.slate.800');
  288. border-color: theme('colors.slate.700');
  289. }
  290. .task-modal-header {
  291. padding: 0.75rem 1rem;
  292. border-bottom: 1px solid theme('colors.slate.200');
  293. background-color: theme('colors.slate.50');
  294. color: theme('colors.slate.800');
  295. }
  296. .dark .task-modal-header {
  297. background-color: theme('colors.slate.700');
  298. border-color: theme('colors.slate.700');
  299. color: theme('colors.slate.100');
  300. }
  301. .task-modal-body {
  302. padding: 1rem;
  303. font-size: 0.875rem;
  304. }
  305. .task-modal-footer {
  306. padding: 0.5rem 1rem 0.75rem;
  307. display: flex;
  308. justify-content: flex-end;
  309. gap: 0.5rem;
  310. border-top: 1px solid theme('colors.slate.100');
  311. background-color: theme('colors.slate.50');
  312. }
  313. .dark .task-modal-footer {
  314. background-color: theme('colors.slate.700');
  315. border-color: theme('colors.slate.700');
  316. }
  317. .task-pickup-active {
  318. background-color: theme('colors.amber.100') !important;
  319. outline: 2px dashed theme('colors.amber.400');
  320. outline-offset: -2px;
  321. }
  322. .dark .task-pickup-active {
  323. background-color: theme('colors.amber.900') !important;
  324. outline-color: theme('colors.amber.500');
  325. }
  326. .task-pickup-indicator {
  327. position: absolute;
  328. height: 2px;
  329. background-color: theme('colors.amber.500');
  330. z-index: 65;
  331. pointer-events: none;
  332. box-shadow: 0 0 0 1px rgb(255 255 255 / 0.5);
  333. }
  334. /* Title cell layout — three columns so the info icon ends up at the
  335. same horizontal offset across rows, regardless of how many ref
  336. icons or whether the URL icon are present. The middle column is
  337. fixed-width and clips overflow so its width never bleeds into the
  338. info-icon column. */
  339. .task-title-grid {
  340. display: grid;
  341. grid-template-columns: minmax(0, 1fr) 4.25rem auto;
  342. gap: 0.375rem;
  343. align-items: center;
  344. }
  345. .task-title-mid {
  346. display: flex;
  347. align-items: center;
  348. gap: 0.125rem;
  349. justify-content: flex-end;
  350. overflow: hidden;
  351. min-width: 0;
  352. }
  353. .task-refs {
  354. flex-wrap: nowrap;
  355. overflow: hidden;
  356. }
  357. /* The reference / info icons stay quiet (slate-400) until hovered.
  358. Matches the existing URL-link icon weight. */
  359. .task-info-trigger:focus-visible,
  360. .task-ref-icon:focus-visible {
  361. outline: 2px solid theme('colors.slate.400');
  362. outline-offset: 1px;
  363. }
  364. /* Info popover — body-attached, opens to the right of the trigger,
  365. vertically centred on it (positioning set by JS). Mirrors the
  366. cellPopover close model: outside-click, Escape, scroll/resize,
  367. and mouseleave-grace 250 ms. */
  368. .task-info-popover {
  369. position: absolute;
  370. z-index: 55;
  371. width: 22rem;
  372. max-width: calc(100vw - 1rem);
  373. padding: 0.75rem 0.875rem;
  374. border-radius: 0.5rem;
  375. border: 1px solid theme('colors.slate.200');
  376. background-color: theme('colors.white');
  377. color: theme('colors.slate.700');
  378. font-size: 0.825rem;
  379. box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.12), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  380. }
  381. .dark .task-info-popover {
  382. border-color: theme('colors.slate.700');
  383. background-color: theme('colors.slate.800');
  384. color: theme('colors.slate.100');
  385. }
  386. .task-info-title {
  387. font-weight: 600;
  388. font-size: 0.9rem;
  389. color: theme('colors.slate.800');
  390. margin-bottom: 0.4rem;
  391. word-break: break-word;
  392. }
  393. .dark .task-info-title {
  394. color: theme('colors.slate.100');
  395. }
  396. .task-info-desc {
  397. white-space: pre-wrap;
  398. word-break: break-word;
  399. max-height: 12rem;
  400. overflow-y: auto;
  401. margin-bottom: 0.5rem;
  402. }
  403. .task-info-url {
  404. margin-bottom: 0.5rem;
  405. font-size: 0.8rem;
  406. word-break: break-all;
  407. }
  408. .task-info-url a {
  409. color: theme('colors.blue.700');
  410. text-decoration: underline;
  411. }
  412. .dark .task-info-url a {
  413. color: theme('colors.blue.300');
  414. }
  415. .task-info-refs-head {
  416. font-size: 0.7rem;
  417. text-transform: uppercase;
  418. letter-spacing: 0.05em;
  419. color: theme('colors.slate.500');
  420. margin-bottom: 0.25rem;
  421. }
  422. .dark .task-info-refs-head {
  423. color: theme('colors.slate.400');
  424. }
  425. .task-info-refs {
  426. list-style: none;
  427. margin: 0;
  428. padding: 0;
  429. display: flex;
  430. flex-direction: column;
  431. gap: 0.15rem;
  432. }
  433. .task-info-refs a {
  434. color: theme('colors.slate.700');
  435. text-decoration: none;
  436. }
  437. .task-info-refs a:hover {
  438. text-decoration: underline;
  439. }
  440. .dark .task-info-refs a {
  441. color: theme('colors.slate.200');
  442. }
  443. }