1
0

style.css 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. :root {
  2. --background-primary: #f9f9f9;
  3. --background-secondary: #fff;
  4. --background-tertiary: #ddd;
  5. --background-accent: rgba(74, 144, 226, 0.75);
  6. --background-info: var(--background-accent);
  7. --background-success: rgba(76, 175, 80, 0.8);
  8. --background-warning: rgba(255, 152, 0, 0.8);
  9. --background-error: rgba(229, 57, 53, 0.8);
  10. --theme-primary: #6A9AD9;
  11. --theme-primary-hover: #A0CCF2;
  12. --theme-secondary: #C4B4A0;
  13. --theme-secondary-hover: #4E453F;
  14. --color-info: var(--theme-primary);
  15. --color-success: #4CAF50CC;
  16. --color-warning: #FF9800CC;
  17. --color-error: #E53935CC;
  18. --text-primary: #333;
  19. --text-secondary: #fff;
  20. --border-primary: var(--background-tertiary);
  21. --border-secondary: grey;
  22. --border-accent: var(--theme-primary);
  23. --border-hover: var(--theme-primary-hover);
  24. --shadow-primary: 0 0 20px var(--border-secondary);
  25. --transition-fast: 0.1s ease-in-out;
  26. --transition-medium: 0.250s ease;
  27. --transition-slow: 1s ease;
  28. }
  29. /* General
  30. /* General Styling */
  31. body {
  32. margin: 0;
  33. font-family: 'Roboto', sans-serif;
  34. background: var(--background-primary);
  35. display: flex;
  36. flex-direction: column;
  37. position: relative;
  38. }
  39. body * {
  40. box-sizing: border-box;
  41. }
  42. h1, h2 {
  43. margin: 0;
  44. }
  45. header {
  46. position: sticky;
  47. height: 50px;
  48. top: 0;
  49. z-index: 10;
  50. background: var(--background-primary);
  51. display: flex;
  52. justify-content: center;
  53. align-items: center;
  54. }
  55. h1, h2 {
  56. color: var(--theme-secondary-hover);
  57. transition: var(--transition-slow) color;
  58. }
  59. h3 {
  60. margin: 10px 0;
  61. }
  62. /* Inputs */
  63. input, select {
  64. display: block;
  65. width: 100%;
  66. padding: 10px;
  67. margin-bottom: 10px;
  68. border: 1px solid var(--border-primary);
  69. border-radius: 5px;
  70. font-size: 1rem;
  71. }
  72. /* Custom Input Wrapper */
  73. .custom-input {
  74. display: flex;
  75. align-items: center;
  76. gap: 10px;
  77. font-size: 1rem;
  78. color: var(--text-primary);
  79. cursor: pointer;
  80. flex: 1 1 auto;
  81. }
  82. /* Hide the Native Input */
  83. .custom-input input {
  84. display: none;
  85. }
  86. /* Checkbox and Radio Styles */
  87. .custom-checkbox,
  88. .custom-radio {
  89. display: inline-block;
  90. width: 20px;
  91. height: 20px;
  92. border: 2px solid var(--theme-primary);
  93. background-color: var(--background-secondary);
  94. position: relative;
  95. transition: background-color 0.3s ease, border-color 0.3s ease;
  96. }
  97. /* Checkbox Specific */
  98. .custom-checkbox {
  99. border-radius: 4px;
  100. }
  101. .custom-checkbox::after {
  102. content: '';
  103. width: 10px;
  104. height: 10px;
  105. background-color: var(--theme-primary);
  106. position: absolute;
  107. top: 50%;
  108. left: 50%;
  109. transform: translate(-50%, -50%) scale(0);
  110. transition: transform 0.2s ease-in-out;
  111. }
  112. /* Radio Specific */
  113. .custom-radio {
  114. border-radius: 50%;
  115. }
  116. .custom-radio::after {
  117. content: '';
  118. width: 10px;
  119. height: 10px;
  120. background-color: var(--theme-primary);
  121. position: absolute;
  122. top: 50%;
  123. left: 50%;
  124. transform: translate(-50%, -50%) scale(0);
  125. border-radius: 50%;
  126. transition: transform 0.2s ease-in-out;
  127. }
  128. /* Checked State */
  129. .custom-input input:checked + .custom-checkbox::after,
  130. .custom-input input:checked + .custom-radio::after {
  131. transform: translate(-50%, -50%) scale(1);
  132. }
  133. .custom-input input:checked + .custom-checkbox,
  134. .custom-input input:checked + .custom-radio {
  135. background-color: var(--theme-primary);
  136. border-color: var(--theme-primary-hover);
  137. }
  138. /* Focus State */
  139. .custom-input input:focus-visible + .custom-checkbox,
  140. .custom-input input:focus-visible + .custom-radio {
  141. outline: 2px dashed var(--theme-primary);
  142. outline-offset: 2px;
  143. }
  144. /* Hover Effects */
  145. .custom-checkbox:hover,
  146. .custom-radio:hover {
  147. border-color: var(--theme-primary-hover);
  148. }
  149. /* Buttons */
  150. button {
  151. background: var(--theme-primary);
  152. color: var(--text-secondary);
  153. padding: 10px 15px;
  154. border: none;
  155. font-weight: bold;
  156. border-radius: 5px;
  157. cursor: pointer;
  158. font-size: 1rem;
  159. transition: background 0.3s ease,color 0.3s ease;
  160. }
  161. button:not(.close-button, .fullscreen-button, .move-button, .remove-button):hover {
  162. background: var(--background-info);
  163. }
  164. button.cancel {
  165. flex-grow: 0;
  166. }
  167. button.cancel:hover {
  168. background: var(--color-error);
  169. }
  170. button.cta:hover {
  171. background: var(--color-success);
  172. }
  173. button.warn:hover {
  174. background: var(--color-warning);
  175. }
  176. button.warning:hover{}
  177. /* App Layout */
  178. .app {
  179. min-height: calc(100vh - 110px);
  180. display: flex;
  181. flex-direction: column;
  182. }
  183. .hidden:not(.sticky) {
  184. display: none !important;
  185. }
  186. /* Tabs */
  187. .tab-content {
  188. display: none;
  189. flex: 1;
  190. overflow-y: auto;
  191. background: var(--background-secondary);
  192. }
  193. .tab-content.active {
  194. display: flex;
  195. position: relative;
  196. flex-direction: column;
  197. }
  198. section {
  199. padding: 15px;
  200. display: flex;
  201. flex-direction: column;
  202. }
  203. section.main {
  204. flex-grow: 1;
  205. }
  206. section.debug {
  207. flex-direction: row;
  208. align-items: center;
  209. justify-content: space-between;
  210. }
  211. section.version {
  212. flex-direction: row;
  213. justify-content: space-between;
  214. flex-wrap: wrap;
  215. }
  216. .version .header {
  217. width: 100%;
  218. }
  219. .version #motor_selection h3 {
  220. width: 100%;
  221. flex-grow: 1;
  222. }
  223. .version #motor_selection {
  224. display: flex;
  225. flex-direction: row;
  226. flex-wrap: wrap;
  227. width: 100%;
  228. }
  229. .version select#manual_motor_type {
  230. margin: 0 20px 0 0;
  231. flex: 1;
  232. }
  233. section.sticky {
  234. position: fixed;
  235. background-color: rgba(255, 255, 255, 0.5);
  236. backdrop-filter: blur(10px);
  237. bottom: 60px;
  238. border-top: 1px solid var(--border-primary);
  239. box-shadow: var(--shadow-primary);
  240. transform: translateY(0);
  241. transition: 250ms transform, 250ms height;
  242. visibility: visible;
  243. max-height: 60vh;
  244. width: 100%;
  245. z-index: 10;
  246. }
  247. section.sticky.fullscreen {
  248. position: fixed;
  249. top: 0;
  250. left: 0;
  251. width: 100vw;
  252. max-height: none;
  253. }
  254. section.sticky.hidden {
  255. transform: translateY(100%);
  256. visibility: hidden;
  257. width: 100%;
  258. position: absolute;
  259. overflow:hidden;
  260. height: 0;
  261. padding: 0;
  262. }
  263. section .header {
  264. position: relative;
  265. display: flex;
  266. justify-content: space-between;
  267. align-items: center;
  268. margin-bottom: 10px;
  269. }
  270. section .header h2 {
  271. flex-grow: 1;
  272. }
  273. /* Close Button Styling */
  274. .close-button,
  275. .fullscreen-button {
  276. background: none;
  277. border: none;
  278. font-size: 1.5rem;
  279. font-weight: bold;
  280. color: var(--text-primary);
  281. cursor: pointer;
  282. line-height: 1;
  283. padding: 0;
  284. height: 100%;
  285. width: auto;
  286. aspect-ratio: 1 / 1;
  287. display: flex;
  288. justify-content: center;
  289. align-items: center;
  290. margin-left: 10px;
  291. }
  292. .close-button:hover {
  293. color: var(--color-error);
  294. }
  295. .fullscreen-button:hover {
  296. color: var(--color-warning);
  297. }
  298. section .header .add-button {
  299. height: 35px;
  300. width: 35px;
  301. font-size: 1.5rem;
  302. padding: 0;
  303. }
  304. /* Playlist */
  305. .add-to-playlist {
  306. margin-top: 15px;
  307. }
  308. .add-to-playlist button {
  309. margin-bottom: 10px;
  310. }
  311. .add-to-container {
  312. display: flex;
  313. flex-wrap: wrap;
  314. margin-bottom: 20px;
  315. }
  316. #add-to-playlist-container select,
  317. #add-to-playlist-container button {
  318. margin-top: 10px;
  319. display: block;
  320. width: 100%;
  321. }
  322. .playlist-parameters {
  323. display: flex;
  324. flex-direction: column;
  325. gap: 10px;
  326. }
  327. .playlist-parameters .row {
  328. display: flex;
  329. gap: 10px;
  330. }
  331. #clear_pattern {
  332. margin: 0;
  333. }
  334. .playlist-parameters .input-group input,
  335. .playlist-parameters .input-group select {
  336. width: 100%; /* Ensure inputs/selects stretch to full width */
  337. padding: 10px;
  338. border: 1px solid var(--border-primary);
  339. border-radius: 5px;
  340. font-size: 1rem;
  341. }
  342. .empty-placeholder {
  343. color: gray;
  344. font-style: italic;
  345. text-align: center;
  346. padding: 10px;
  347. }
  348. /* Style for the filename span */
  349. .filename {
  350. flex-grow: 1; /* Use available space */
  351. font-size: 1rem;
  352. color: var(--text-primary);
  353. margin-right: 10px; /* Space between filename and buttons */
  354. word-wrap: break-word;
  355. width: 100%;
  356. display: flex;
  357. align-items: center;
  358. }
  359. /* File List */
  360. .file-list {
  361. list-style: none;
  362. padding: 0;
  363. margin: 0;
  364. border: 1px solid var(--border-primary);
  365. border-radius: 5px;
  366. overflow-y: auto;
  367. background: var(--background-primary);
  368. flex-grow: 1;
  369. }
  370. .file-list li {
  371. display: flex;
  372. padding: 10px;
  373. border-bottom: 1px solid var(--border-primary);
  374. cursor: pointer;
  375. transition: background-color 0.3s ease;
  376. }
  377. .file-list li:hover {
  378. background-color: #f1f1f1;
  379. }
  380. .file-list li.selected {
  381. background: var(--theme-primary);
  382. color: var(--text-secondary);
  383. font-weight: bold;
  384. }
  385. .file-list li.selected .filename {
  386. font-weight: bold;
  387. color: var(--text-secondary);
  388. }
  389. .file-list button {
  390. margin-left: 5px;
  391. background: none;
  392. color: black;
  393. font-weight: bold;
  394. height: 40px;
  395. width: 40px;
  396. flex: 0 0 auto;
  397. display: flex;
  398. justify-content: center;
  399. align-items: center;
  400. }
  401. .file-list button:hover:not(:focus) {
  402. background: var(--background-primary);
  403. box-shadow: inset 0 0 4px var(--border-secondary);
  404. }
  405. .file-list button.remove-button {
  406. color: var(--color-error);
  407. }
  408. .title-container {
  409. display: flex;
  410. justify-content: space-between;
  411. align-items: center;
  412. }
  413. .rename-button {
  414. margin-left: 10px;
  415. background: var(--theme-primary-hover);
  416. color: var(--text-secondary);
  417. border: none;
  418. border-radius: 5px;
  419. padding: 5px 10px;
  420. cursor: pointer;
  421. transition: background 0.3s ease;
  422. }
  423. .rename-button:hover {
  424. background: #285A8E;
  425. }
  426. /* Bottom Navigation */
  427. .bottom-nav {
  428. display: flex;
  429. position: sticky;
  430. justify-content: space-around;
  431. bottom: 0;
  432. height: 60px;
  433. width: 100%;
  434. border-top: 1px solid var(--theme-primary);
  435. flex-wrap: wrap;
  436. z-index: 10;
  437. }
  438. .tab-button {
  439. flex: 1;
  440. padding: 20px 10px;
  441. text-align: center;
  442. font-size: 1rem;
  443. font-weight: bold;
  444. color: var(--text-secondary);
  445. background: none;
  446. border: none;
  447. cursor: pointer;
  448. transition: background 0.3s ease;
  449. background: var(--background-info);
  450. backdrop-filter: blur(2px);
  451. border-radius: 0;
  452. }
  453. .bottom-nav .tab-button.active {
  454. background: rgba(255, 255, 255, 0.75);
  455. color: var(--theme-primary);
  456. }
  457. /* Quick Action Buttons */
  458. .action-buttons {
  459. display: flex;
  460. gap: 10px;
  461. flex-wrap: wrap;
  462. width: 100%;
  463. }
  464. .action-buttons button {
  465. flex: 1;
  466. }
  467. .action-buttons button.cta {
  468. flex-grow: 1;
  469. }
  470. button#debug_button {
  471. width: 40px;
  472. padding: 0;
  473. height: 40px;
  474. background: transparent;
  475. font-size: 1.5rem;
  476. margin-left: 40px;
  477. flex: 0 0 auto;
  478. transition: 250ms all;
  479. }
  480. button#debug_button:hover,
  481. button#debug_button.active {
  482. box-shadow: inset 0 0 4px var(--border-secondary);
  483. }
  484. #settings-tab button.cancel {
  485. flex-basis: 100%;
  486. }
  487. /* Preview Canvas */
  488. #patternPreviewCanvas {
  489. width: 100%;
  490. max-width: 300px;
  491. aspect-ratio: 1/1;
  492. border: 1px solid var(--border-primary);
  493. background: var(--theme-secondary);
  494. border-radius: 100%;
  495. padding: 30px;
  496. }
  497. #pattern-preview {
  498. display: flex;
  499. flex-direction: column;
  500. align-items: center;
  501. margin-bottom: 20px;
  502. }
  503. #pattern-preview-container.fullscreen #patternPreviewCanvas {
  504. width: initial;
  505. max-width: calc(100vw - 30px);
  506. }
  507. /* Debug Log */
  508. #status_log {
  509. background: #000;
  510. color: var(--text-secondary);
  511. font-family: monospace;
  512. font-size: 0.9rem;
  513. border-top: 1px solid var(--border-primary);
  514. padding: 10px;
  515. max-height: 200px;
  516. overflow-y: scroll;
  517. display: none;
  518. width: 100%;
  519. }
  520. #status_log p {
  521. margin: 0;
  522. }
  523. .control-group {
  524. display: flex;
  525. margin-bottom: 10px;
  526. flex-wrap: wrap;
  527. width: 100%;
  528. align-items: center;
  529. justify-content: space-between;
  530. gap: 0 10px;
  531. }
  532. .control-group input {
  533. margin-bottom: 0;
  534. }
  535. .control-group h3 {
  536. width: 100%;
  537. }
  538. .control-group .item {
  539. display: flex;
  540. align-items: center;
  541. flex: 1;
  542. }
  543. .control-group .item.cta {
  544. justify-content: flex-end;
  545. }
  546. .control-group .item.column {
  547. flex-direction: column;
  548. text-align: center;
  549. }
  550. .control-group .item label {
  551. padding: 5px;
  552. }
  553. #serial_ports_container > * {
  554. display: inline-block;
  555. }
  556. #serial_ports_container select {
  557. margin: 10px;
  558. flex-basis: 100px;
  559. flex-grow: 0;
  560. }
  561. #serial_ports {
  562. width: auto;
  563. min-width: 200px;
  564. }
  565. #serial_status_container {
  566. margin-bottom: 10px;
  567. }
  568. #serial_status_header::before {
  569. content: '';
  570. width: 20px;
  571. height: 20px;
  572. border-radius: 50%;
  573. margin-right: 8px;
  574. background-color: var(--text-primary);
  575. display: inline-block;
  576. transition: var(--transition-slow) background-color;
  577. }
  578. #serial_status_header.connected::before {
  579. background-color: var(--color-success);
  580. }
  581. #serial_status_header.not-connected::before {
  582. background-color: var(--color-error);
  583. }
  584. #serial_ports_buttons {
  585. display: inline-block;
  586. }
  587. .status.connected {
  588. color: var(--color-success);
  589. font-weight: bold;
  590. }
  591. .status.not-connected {
  592. color: var(--color-error);
  593. font-weight: bold;
  594. }
  595. /* Speed Control Section */
  596. .speed-control {
  597. display: flex;
  598. }
  599. .speed-control label {
  600. font-weight: bold;
  601. font-size: 1rem;
  602. color: var(--text-primary);
  603. flex-shrink: 0;
  604. }
  605. .speed-control input[type="number"] {
  606. width: 100px; /* Consistent input width */
  607. padding: 8px;
  608. font-size: 1rem;
  609. border: 1px solid var(--border-primary);
  610. border-radius: 5px;
  611. outline: none;
  612. transition: all 0.3s ease;
  613. }
  614. input[type="number"]:focus {
  615. border-color: var(--theme-primary);
  616. box-shadow: 0 0 4px var(--background-info);
  617. }
  618. #speed_status {
  619. margin-top: 10px;
  620. font-size: 0.9rem;
  621. }
  622. #serial_ports_container > * {
  623. display: inline-block;
  624. }
  625. #serial_ports_container select {
  626. margin: 10px;
  627. flex-basis: 100px;
  628. flex-grow: 0;
  629. }
  630. #serial_ports {
  631. width: auto;
  632. min-width: 200px;
  633. }
  634. #serial_status_container,
  635. #serial_ports_buttons {
  636. display: inline-block;
  637. }
  638. /* Notification Styles */
  639. .notification {
  640. display: flex;
  641. position: absolute;
  642. top: 0;
  643. left: 0;
  644. font-weight: bold;
  645. z-index: 1000;
  646. color: var(--text-secondary);
  647. width: 100%;
  648. height: 100%;
  649. justify-content: center;
  650. align-items: center;
  651. backdrop-filter: blur(2px);
  652. opacity: 0;
  653. transition: opacity 250ms ease-in-out;
  654. }
  655. .notification.show {
  656. opacity: 1; /* Fully visible */
  657. }
  658. .notification .close-button {
  659. color: var(--text-secondary);
  660. font-size: 2rem;
  661. top: 0;
  662. right: 0;
  663. }
  664. /* Notification Types */
  665. .notification.success { background-color: var(--background-success); }
  666. .notification.warning { background-color: var(--background-warning); }
  667. .notification.error { background-color: var(--background-error); }
  668. .notification.info { background-color: var(--background-info); }
  669. .footer {
  670. align-items: center;
  671. display: flex;
  672. flex-wrap: wrap;
  673. justify-content: space-between;
  674. margin-bottom: 20px;
  675. width: 100%;
  676. }
  677. #github {
  678. align-content: center;
  679. display: flex;
  680. font-size: 0.8em;
  681. }
  682. #github img {
  683. margin: 0 5px
  684. }
  685. /* Responsive Design */
  686. @media (max-width: 1023px) {
  687. body {
  688. font-size: 0.9rem;
  689. }
  690. .tab-button {
  691. font-size: 0.9rem;
  692. }
  693. .footer {
  694. display: none;
  695. }
  696. button.cancel {
  697. background: var(--color-error);
  698. }
  699. button.cta {
  700. background: var(--color-success);
  701. }
  702. button.warn {
  703. background: var(--color-warning);
  704. }
  705. button.cancel:hover,
  706. button.warn:hover,
  707. button.cta:hover {
  708. background: var(--theme-primary);
  709. }
  710. }
  711. /* On larger screens, display all tabs in a 3-column grid */
  712. @media screen and (min-width: 1024px) {
  713. .app {
  714. display: grid;
  715. grid-template-columns: repeat(3, 1fr);
  716. gap: 0 16px;
  717. height: calc(100vh - 60px);
  718. }
  719. .bottom-nav {
  720. display: none;
  721. }
  722. #status_log {
  723. grid-column: span 3;
  724. align-self: flex-end;
  725. height: 100%;
  726. }
  727. section.sticky {
  728. position: sticky;
  729. bottom: 0;
  730. }
  731. /* Show all tabs in grid layout */
  732. .tab-content {
  733. display: flex !important; /* Always display tab-content */
  734. flex-direction: column;
  735. border: 1px solid var(--border-primary);
  736. background-color: var(--background-primary);
  737. border-radius: 8px;
  738. overflow-y: auto;
  739. overflow-x: hidden;
  740. position: relative;
  741. }
  742. }