mirror of
				https://github.com/kevinveenbirkenbach/computer-playbook.git
				synced 2025-10-31 10:19:09 +00:00 
			
		
		
		
	feat(frontend): rename inj roles to sys-front-*, add sys-svc-cdn, cache-busting lookup
Introduce sys-svc-cdn (cdn_paths/cdn_urls/cdn_dirs) and ensure CDN directories + latest symlink. Rename sys-srv-web-inj-* → sys-front-inj-*; update includes/templates; serve shared/per-app CSS & JS via CDN. Add lookup_plugins/local_mtime_qs.py for mtime-based cache busting; split CSS into default.css/bootstrap.css + optional per-app style.css. CSP: use style-src-elem; drop unsafe-inline for styles. Services: fix SYS_SERVICE_ALL_ENABLED bool and controlled flush. BREAKING CHANGE: role names changed; replace includes and references accordingly. Conversation: https://chatgpt.com/share/68b55494-9ec4-800f-b559-44707029141d
This commit is contained in:
		
							
								
								
									
										69
									
								
								roles/sys-front-inj-css/templates/css/bootstrap.css.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								roles/sys-front-inj-css/templates/css/bootstrap.css.j2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
|  | ||||
| /* Buttons (Background, Text, Border, and Shadow) | ||||
|    Now using a button background that is only slightly darker than the overall background */ | ||||
| html[native-dark-active] .btn, .btn { | ||||
|     background-color: var(--color-01-87); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-70), var(--color-01-91), var(--color-01-95), var(--color-01-95)); | ||||
|     color: var(--color-01-50); | ||||
|     border-color: var(--color-01-80); | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| /* Navigation (Background and Text Colors) */ | ||||
| .navbar, .navbar-light, .navbar-dark, .navbar.bg-light { | ||||
|     background-color: var(--color-01-90); | ||||
|     /* New Gradient based on original background (90 -5, 90, 90 +1, 90 +5) */ | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-85), var(--color-01-90), var(--color-01-91), var(--color-01-95)); | ||||
|     color: var(--color-01-50); | ||||
|     border-color: var(--color-01-85); | ||||
| } | ||||
|  | ||||
| .navbar a { | ||||
|     color: var(--color-01-40); | ||||
| } | ||||
|  | ||||
| .navbar a.dropdown-item { | ||||
|     color: var(--color-01-43); | ||||
| } | ||||
|  | ||||
| /* Cards / Containers (Background, Border, and Shadow) | ||||
|    Cards now use a slightly lighter background and a bold, clear shadow */ | ||||
| .card { | ||||
|     background-color: var(--color-01-90); | ||||
|     /* New Gradient based on original background (90 -5, 90, 90 +1, 90 +5) */ | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-85), var(--color-01-90), var(--color-01-91), var(--color-01-95)); | ||||
|     border-color: var(--color-01-85); | ||||
|     color: var(--color-01-12); | ||||
| } | ||||
|  | ||||
| .card-body { | ||||
|     color: var(--color-01-40); | ||||
| } | ||||
|  | ||||
| /* Dropdown Menu and Submenu (Background, Text, and Shadow) */ | ||||
| .navbar .dropdown-menu, | ||||
| .nav-item .dropdown-menu { | ||||
|     background-color: var(--color-01-80); | ||||
|     /* New Gradient based on original background (80 -5, 80, 80 +1, 80 +5) */ | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-80), var(--color-01-81), var(--color-01-85)); | ||||
|     color: var(--color-01-40); | ||||
| } | ||||
|  | ||||
| .navbar-nav { | ||||
|     --bs-nav-link-hover-color: var(--color-01-17); | ||||
| } | ||||
|  | ||||
| .dropdown-item { | ||||
|     color: var(--color-01-40); | ||||
|     background-color: var(--color-01-80); | ||||
|     /* New Gradient based on original background (80 -5, 80, 80 +1, 80 +5) */ | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-80), var(--color-01-81), var(--color-01-85)); | ||||
| } | ||||
|  | ||||
| .dropdown-item:hover, | ||||
| .dropdown-item:focus { | ||||
|     background-color: var(--color-01-65); | ||||
|     /* New Gradient based on original background (65 -5, 65, 65 +1, 65 +5) */ | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-60), var(--color-01-65), var(--color-01-66), var(--color-01-70)); | ||||
|     color: var(--color-01-40); | ||||
| } | ||||
							
								
								
									
										297
									
								
								roles/sys-front-inj-css/templates/css/default.css.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								roles/sys-front-inj-css/templates/css/default.css.j2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,297 @@ | ||||
| /*** | ||||
|  | ||||
| Global Theming Styles – Color and Shadow Variables  | ||||
|  | ||||
| HINT:  | ||||
| - Better overwritte CSS variables instead of individual elements. | ||||
| - Don't use !important. If possible use a specific selector. | ||||
|  | ||||
| */ | ||||
|  | ||||
| {% if design.font.import_url %} | ||||
| @import url('{{ design.font.import_url }}'); | ||||
| {% endif %} | ||||
|  | ||||
| /* Auto-generated by colorscheme-generator */ | ||||
|  | ||||
| :root { | ||||
|   {% for var_name, color in color_palette.items() %} | ||||
|     {{ var_name }}: {{ color }}; | ||||
|   {% endfor %} | ||||
| } | ||||
|  | ||||
| @media (prefers-color-scheme: dark) { | ||||
|   :root { | ||||
|     {% for var_name, color in inverted_color_palette.items() %} | ||||
|       {{ var_name }}: {{ color }}; | ||||
|     {% endfor %} | ||||
|   } | ||||
| } | ||||
|  | ||||
| :root, ::after, ::before, ::backdrop { | ||||
|     /* For Dark Mode Plugin | ||||
|      * @See https://chromewebstore.google.com/detail/dark-mode/dmghijelimhndkbmpgbldicpogfkceaj | ||||
|      */ | ||||
|     --native-dark-accent-color:         var(--color-01-60);  /* was #a9a9a9 */ | ||||
|     --native-dark-bg-color:             var(--color-01-10);  /* was #292929 */ | ||||
|     --native-dark-bg-image-color:       rgba(var(--color-01-rgb-01), 0.10); /* remains the same, or adjust if needed */ | ||||
|     --native-dark-border-color:         var(--color-01-40);  /* was #555555 */ | ||||
|     --native-dark-box-shadow:           0 0 0 1px rgb(var(--color-01-rgb-99), / 10%); | ||||
|     --native-dark-cite-color:           var(--color-01-70);  /* was #92de92 – you might adjust if a green tone is needed */ | ||||
|     --native-dark-fill-color:           var(--color-01-50);  /* was #7d7d7d */ | ||||
|     --native-dark-font-color:           var(--color-01-95);  /* was #dcdcdc */ | ||||
|     --native-dark-link-color:           var(--color-01-80);  /* was #8db2e5 */ | ||||
|     --native-dark-visited-link-color:   var(--color-01-85);  /* was #c76ed7 */ | ||||
| } | ||||
|  | ||||
| /* Bootstrap Overrides (Color/Shadow Variables Only) */ | ||||
| :root { | ||||
|   --bs-black: var(--color-01-01); /* Original tone: Black (#000) */ | ||||
|   --bs-white: var(--color-01-99); /* Original tone: White (#fff) */ | ||||
|   --bs-gray: var(--color-01-50); /* Original tone: Gray (#6c757d) */ | ||||
|   --bs-gray-dark: var(--color-01-20); /* Original tone: Dark Gray (#343a40) */ | ||||
| {% for i in range(1, 10) %} | ||||
| {# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} | ||||
|   {% set gray = i * 100 %} | ||||
|   {% set color = 100 - i * 10 %} | ||||
|   --bs-gray-{{ gray }}: var(--color-01-{{ "%02d" % color }}); | ||||
| {% endfor %} | ||||
|   --bs-primary: var(--color-01-65); /* Original tone: Blue (#0d6efd) */ | ||||
|   --bs-light: var(--color-01-95); /* Original tone: Light (#f8f9fa) */ | ||||
|   --bs-dark: var(--color-01-10); /* Original tone: Dark (#212529) */ | ||||
|   --bs-primary-rgb: var(--color-01-rgb-65); /* Original tone: Blue (13, 110, 253) */ | ||||
|   --bs-secondary-rgb: var(--color-01-rgb-50); /* Original tone: Grayish (#6c757d / 108, 117, 125) */ | ||||
|   --bs-light-rgb: var(--color-01-rgb-95); /* Original tone: Light (248, 249, 250) */ | ||||
|   --bs-dark-rgb: var(--color-01-rgb-10); /* Original tone: Dark (33, 37, 41) */ | ||||
|   --bs-white-rgb: var(--color-01-rgb-99); /* Original tone: White (255, 255, 255) */ | ||||
|   --bs-black-rgb: var(--color-01-rgb-01); /* Original tone: Black (0, 0, 0) */ | ||||
|   --bs-body-color-rgb: var(--color-01-rgb-10); /* Original tone: Dark (#212529 / 33, 37, 41) */ | ||||
|   --bs-body-bg-rgb: var(--color-01-rgb-99); /* Original tone: White (#fff / 255, 255, 255) */ | ||||
|   --bs-body-color: var(--color-01-10); /* Original tone: Dark (#212529) */ | ||||
|   --bs-body-bg: var(--color-01-99); /* Original tone: White (#fff) */ | ||||
|   --bs-border-color: var(--color-01-85); /* Original tone: Gray (#dee2e6) */ | ||||
|   --bs-link-color: var(--color-01-65); /* Original tone: Blue (#0d6efd) */ | ||||
|   --bs-link-hover-color: var(--color-01-60); /* Original tone: Darker Blue (#0a58ca) */ | ||||
|   --bs-code-color: var(--color-01-55); /* Original tone: Pink (#d63384) */ | ||||
|   --bs-highlight-bg: var(--color-01-93); /* Original tone: Light Yellow (#fff3cd) */ | ||||
|   --bs-list-group-bg: var(--color-01-40); | ||||
|   --bs-emphasis-color: var(--color-01-01); /* Gemappt von #000 */ | ||||
|   --bs-emphasis-color-rgb: var(--color-01-rgb-01); /* Gemappt von 0, 0, 0 */ | ||||
|   --bs-secondary-color: rgba(var(--color-01-rgb-10), 0.75); /* Gemappt von rgba(33, 37, 41, 0.75) */ | ||||
|   --bs-secondary-color-rgb: var(--color-01-rgb-10); /* Gemappt von 33, 37, 41 */ | ||||
|   --bs-secondary-bg: var(--color-01-90); /* Gemappt von #e9ecef */ | ||||
|   --bs-secondary-bg-rgb: var(--color-01-rgb-90); /* Gemappt von 233, 236, 239 */ | ||||
|   --bs-tertiary-color: rgba(var(--color-01-rgb-10), 0.5); /* Gemappt von rgba(33, 37, 41, 0.5) */ | ||||
|   --bs-tertiary-color-rgb: var(--color-01-rgb-10); /* Gemappt von 33, 37, 41 */ | ||||
|   --bs-tertiary-bg: var(--color-01-95); /* Gemappt von #f8f9fa */ | ||||
|   --bs-tertiary-bg-rgb: var(--color-01-rgb-95); /* Gemappt von 248, 249, 250 */ | ||||
|   --bs-link-color-rgb: var(--color-01-rgb-65); /* Gemappt von 13, 110, 253 */ | ||||
|   --bs-link-hover-color-rgb: var(--color-01-rgb-60); /* Gemappt von 10, 88, 202 */ | ||||
|   --bs-highlight-color: var(--color-01-10); /* Gemappt von #212529 */ | ||||
|   --bs-border-color-translucent: rgba(var(--color-01-rgb-01), 0.175); /* Gemappt von rgba(0, 0, 0, 0.175) */ | ||||
|   --bs-focus-ring-color: rgba(var(--color-01-rgb-65), 0.25); /* Gemappt von rgba(13, 110, 253, 0.25) */ | ||||
|    | ||||
|   --bs-table-color: var(--bs-emphasis-color); | ||||
|   --bs-table-bg: var(--color-01-99);          /* White (#fff) */ | ||||
|   --bs-table-border-color: var(--color-01-99);  /* White (#fff) */ | ||||
|   --bs-table-striped-bg: var(--color-01-85);    /* Light Gray (entspricht ca. #dee2e6) */ | ||||
|   --bs-table-hover-color: var(--color-01-01);   /* Black (#000) */ | ||||
|   --bs-table-hover-bg: rgba(var(--bs-emphasis-color-rgb), 0.075); | ||||
| } | ||||
|  | ||||
| /* Global Defaults (Colors Only) */ | ||||
| body, html[native-dark-active] { | ||||
|     background-color: var(--color-01-93); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-93), var(--color-01-91), var(--color-01-95), var(--color-01-93)); | ||||
|     background-attachment: fixed; | ||||
|     color: var(--color-01-40); | ||||
|     font-family: {{design.font.type}}; | ||||
| } | ||||
|  | ||||
| {# All links (applies to all anchor elements regardless of state) #} | ||||
| a { | ||||
|     color: var(--color-01-50); | ||||
| } | ||||
|  | ||||
| {# Unvisited links (applies only to links that have not been visited) #} | ||||
| a:link { | ||||
|     color: var(--color-01-55); | ||||
| } | ||||
|  | ||||
| {# Visited links (applies only to links that have been visited) #} | ||||
| a:visited { | ||||
|     color: var(--color-01-45); | ||||
| } | ||||
|  | ||||
| {# Hover state (applies when the mouse pointer is over the link) #} | ||||
| a:hover { | ||||
|     color: var(--color-01-60); | ||||
| } | ||||
|  | ||||
| {# Active state (applies during the time the link is being activated, e.g., on click) #} | ||||
| a:active { | ||||
|     color: var(--color-01-65); | ||||
| } | ||||
|  | ||||
| /** Set default buttons transparent **/  | ||||
| html[native-dark-active] button, button{ | ||||
|      background-color: var(--color-01-87); | ||||
| } | ||||
|  | ||||
| button:hover, .btn:hover { | ||||
|     filter: brightness(0.9); | ||||
| } | ||||
|  | ||||
| /* {# Invalid state: when the input value fails validation criteria. Use danger color for error indication. #} */ | ||||
| input:invalid, | ||||
| textarea:invalid, | ||||
| select:invalid { | ||||
|     background-color: var(--color-01-01); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-01), var(--color-01-10)); | ||||
|     /* Use Bootstrap danger color for error messages */ | ||||
|     color: var(--bs-danger); | ||||
|     border-color: var(--color-01-20); | ||||
| } | ||||
|  | ||||
| /* {# Valid state: when the input value meets all validation criteria. Use success color for confirmation. #} */ | ||||
| input:valid, | ||||
| textarea:valid, | ||||
| select:valid { | ||||
|     background-color: var(--color-01-80); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-80), var(--color-01-90)); | ||||
|     /* Use Bootstrap success color for confirmation messages */ | ||||
|     color: var(--bs-success); | ||||
|     border-color: var(--color-01-70); | ||||
| } | ||||
|  | ||||
| /* {# Required field: applied to elements that must be filled out by the user. Use warning color for emphasis. #} */ | ||||
| input:required, | ||||
| textarea:required, | ||||
| select:required { | ||||
|     background-color: var(--color-01-50); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-50), var(--color-01-60)); | ||||
|     /* Use Bootstrap warning color to indicate a required field */ | ||||
|     color: var(--bs-warning); | ||||
|     border-color: var(--color-01-70); | ||||
| } | ||||
|  | ||||
| /* {# Optional field: applied to elements that are not mandatory. Use info color to denote additional information. #} */ | ||||
| input:optional, | ||||
| textarea:optional, | ||||
| select:optional { | ||||
|     background-color: var(--color-01-60); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-60), var(--color-01-70)); | ||||
|     /* Use Bootstrap info color to indicate optional information */ | ||||
|     color: var(--bs-info); | ||||
|     border-color: var(--color-01-70); | ||||
| } | ||||
|  | ||||
| /* {# Read-only state: when an element is not editable by the user. #} */ | ||||
| input:read-only, | ||||
| textarea:read-only, | ||||
| select:read-only { | ||||
|     background-color: var(--color-01-80); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-90), var(--color-01-70)); | ||||
|     color: var(--color-01-20); | ||||
|     border-color: var(--color-01-50); | ||||
| } | ||||
|  | ||||
| /* {# Read-write state: when an element is editable by the user. #} */ | ||||
| input:read-write, | ||||
| textarea:read-write, | ||||
| select:read-write { | ||||
|     background-color: var(--color-01-70); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-70), var(--color-01-80)); | ||||
|     color: var(--color-01-40); | ||||
|     border-color: var(--color-01-70); | ||||
| } | ||||
|  | ||||
| /* {# In-range: for inputs with a defined range, when the value is within the allowed limits. #} */ | ||||
| input:in-range, | ||||
| textarea:in-range, | ||||
| select:in-range { | ||||
|     background-color: var(--color-01-70); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-70), var(--color-01-85)); | ||||
|     color: var(--color-01-40); | ||||
|     border-color: var(--color-01-70); | ||||
| } | ||||
|  | ||||
| /* {# Out-of-range: for inputs with a defined range, when the value falls outside the allowed limits. #} */ | ||||
| input:out-of-range, | ||||
| textarea:out-of-range, | ||||
| select:out-of-range { | ||||
|     background-color: var(--color-01-10); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-10), var(--color-01-30)); | ||||
|     color: var(--color-01-10); | ||||
|     border-color: var(--color-01-50); | ||||
| } | ||||
|  | ||||
| /* {# Placeholder-shown: when the input field is displaying its placeholder text. #} */ | ||||
| input:placeholder-shown, | ||||
| textarea:placeholder-shown, | ||||
| select:placeholder-shown { | ||||
|     background-color: var(--color-01-82); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-82), var(--color-01-90)); | ||||
|     color: var(--color-01-40); | ||||
|     border-color: var(--color-01-70); | ||||
| } | ||||
|  | ||||
| /* {# Focus state: when the element is focused by the user. #} */ | ||||
| input:focus, | ||||
| textarea:focus, | ||||
| select:focus { | ||||
|     background-color: var(--color-01-75); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-85)); | ||||
|     color: var(--color-01-40); | ||||
|     border-color: var(--color-01-50); | ||||
| } | ||||
|  | ||||
| /* {# Hover state: when the mouse pointer is over the element. #} */ | ||||
| input:hover, | ||||
| textarea:hover, | ||||
| select:hover { | ||||
|     background-color: var(--color-01-78); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-78), var(--color-01-88)); | ||||
|     color: var(--color-01-40); | ||||
|     border-color: var(--color-01-65); | ||||
| } | ||||
|  | ||||
| /* {# Active state: when the element is being activated (e.g., clicked). #} */ | ||||
| input:active, | ||||
| textarea:active, | ||||
| select:active { | ||||
|     background-color: var(--color-01-68); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-68), var(--color-01-78)); | ||||
|     color: var(--color-01-40); | ||||
|     border-color: var(--color-01-60); | ||||
| } | ||||
|  | ||||
| /* {# Checked state: specifically for radio buttons and checkboxes when selected. #} */ | ||||
| input:checked { | ||||
|     background-color: var(--color-01-90); | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-90), var(--color-01-99)); | ||||
|     color: var(--color-01-40); | ||||
|     border-color: var(--color-01-70); | ||||
| } | ||||
|  | ||||
| option { | ||||
|     background-color: var(--color-01-82); | ||||
|     color: var(--color-01-07); | ||||
| } | ||||
|  | ||||
| /* Tables (Borders and Header Colors) */ | ||||
| th, td { | ||||
|     border-color: var(--color-01-70); | ||||
| } | ||||
|  | ||||
| thead { | ||||
|     background-color: var(--color-01-80); | ||||
|     /* New Gradient based on original background (80 -5, 80, 80 +1, 80 +5) */ | ||||
|     background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-80), var(--color-01-81), var(--color-01-85)); | ||||
|     color: var(--color-01-40); | ||||
| } | ||||
|  | ||||
| /* Headings (Text Color) */ | ||||
| h1, h2, h3, h4, h5, h6, p{ | ||||
|     color: var(--color-01-10); | ||||
| } | ||||
							
								
								
									
										8
									
								
								roles/sys-front-inj-css/templates/head_sub.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								roles/sys-front-inj-css/templates/head_sub.j2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| {% set __css_tpl_dir = [playbook_dir, 'roles', 'sys-front-inj-css', 'templates', 'css'] | path_join %} | ||||
|  | ||||
| {% for css_file in ['default.css','bootstrap.css'] %} | ||||
| <link rel="stylesheet" href="{{ [ cdn_urls.shared.css, css_file, lookup('local_mtime_qs', [__css_tpl_dir, css_file ~ '.j2'] | path_join)] | url_join }}"> | ||||
| {% endfor %} | ||||
| {% if app_style_present | bool %} | ||||
| <link rel="stylesheet" href="{{ [ cdn_urls.role.release.css, 'style.css', lookup('local_mtime_qs', app_style_src)] | url_join }}"> | ||||
| {% endif %} | ||||
		Reference in New Issue
	
	Block a user