diff --git a/QUICKSTART.md b/QUICKSTART.md index 10984ed..7b4176b 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -31,24 +31,45 @@ Or via web UI: Settings β†’ Apps β†’ search "nldesign" β†’ Enable 4. Select your organization (Rijkshuisstijl, Utrecht, etc.) 5. Reload the page -πŸŽ‰ **Done!** Your Nextcloud now uses Dutch government design styling with professional Fira Sans typography. +### 5. Set Background Color (Important!) + +The NL Design app does not set a background color automatically. You must configure it in Nextcloud's theming: + +1. Stay in: **Settings β†’ Administration β†’ Theming** (Nextcloud's main section) +2. Scroll to: **Background and color** section +3. Click on **Color** and enter the background color for your token set: + +| Token Set | Primary Color | Background Color | +|-----------|--------------|------------------| +| **Rijkshuisstijl** | `#154273` (blue) | `#F5F6F7` (light gray) | +| **Utrecht** | `#CC0000` (red) | `#FFFFFF` (white) | +| **Amsterdam** | `#EC0000` (red) | `#FFFFFF` (white) | +| **Den Haag** | `#1A7A3E` (green) | `#FFFFFF` (white) | +| **Rotterdam** | `#00811F` (green) | `#FFFFFF` (white) | + +4. **Click on Background image** β†’ Select **Remove background image** +5. Save changes + +**Note**: Primary colors are set automatically by NL Design when you select a token set. Only the background color needs manual configuration. + +πŸŽ‰ **Done!** Your Nextcloud now uses Dutch government design styling with professional Fira Sans typography and correct colors. ## 🎨 Token Sets Available -| Set | Color | Best For | -|-----|-------|----------| -| **Rijkshuisstijl** | Blue (#154273) | National government | -| **Utrecht** | Red (#cc0000) | Municipality | -| **Amsterdam** | Red (#ec0000) | Municipality | -| **Den Haag** | Green (#1a7a3e) | Municipality | -| **Rotterdam** | Green (#00811f) | Municipality | +| Token Set | Primary Color | Background | Best For | +|-----------|--------------|------------|----------| +| **Rijkshuisstijl** | `#154273` (blue) | `#F5F6F7` | National government | +| **Utrecht** | `#CC0000` (red) | `#FFFFFF` | Municipality | +| **Amsterdam** | `#EC0000` (red) | `#FFFFFF` | Municipality | +| **Den Haag** | `#1A7A3E` (green) | `#FFFFFF` | Municipality | +| **Rotterdam** | `#00811F` (green) | `#FFFFFF` | Municipality | ## βœ… What You Get - βœ… Professional Fira Sans typography - βœ… Official Dutch government colors - βœ… Sharp corners (Rijkshuisstijl) or rounded (municipalities) -- βœ… Clean white backgrounds +- βœ… Configurable background colors via Nextcloud theming - βœ… WCAG AA accessible - βœ… Responsive design - βœ… No build required @@ -98,9 +119,13 @@ docker exec -u 33 nextcloud php occ maintenance:repair ### Colors Wrong? -1. Check which token set is selected -2. Hard reload browser (Ctrl+Shift+R) -3. Clear Nextcloud cache +1. Check which token set is selected in NL Design settings +2. **Check background color** in Nextcloud Theming settings: + - Should be `#F5F6F7` for Rijkshuisstijl + - Should be `#FFFFFF` for other token sets + - Background image should be removed +3. Hard reload browser (Ctrl+Shift+R) +4. Clear Nextcloud cache ## πŸ“– Full Documentation diff --git a/README.md b/README.md index 7a80038..ce6dd9f 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,37 @@ Navigate to **Settings β†’ Administration β†’ Theming** and find the "NL Design Select your preferred design token set and reload the page to see the changes. +### Configuring Background Color + +The NL Design app does not set a background color - this allows you to use Nextcloud's built-in theming system to configure the background color to match your organization's needs. + +**To set the background color:** + +1. Navigate to **Settings β†’ Administration β†’ Theming** (Nextcloud's main theming section, not the NL Design section) +2. Scroll to **Background and color** section +3. Click on **Color** and enter your desired background color +4. **Important**: Also click on **Background image** and select **Remove background image** to ensure a solid color background + +**Recommended colors by token set:** + +| Token Set | Primary Color | Background Color | +|-----------|--------------|------------------| +| **Rijkshuisstijl** | `#154273` (donkerblauw) | `#F5F6F7` (light gray) | +| **Utrecht** | `#CC0000` (red) | `#FFFFFF` (white) | +| **Amsterdam** | `#EC0000` (red) | `#FFFFFF` (white) | +| **Den Haag** | `#1A7A3E` (green) | `#FFFFFF` (white) | +| **Rotterdam** | `#00811F` (green) | `#FFFFFF` (white) | + +**Note**: The primary colors are automatically applied by the NL Design app when you select a token set. You only need to configure the background color manually in Nextcloud's theming settings. + +**Why does NL Design not set the background color?** + +By delegating background color management to Nextcloud's theming system, organizations can: +- Use their own custom background colors +- Easily change backgrounds without modifying app code +- Maintain compatibility with Nextcloud's theming API +- Allow different backgrounds for different user groups or instances + ## Fonts This app uses **Fira Sans** as an open-source alternative to the proprietary government fonts: diff --git a/appinfo/info.xml b/appinfo/info.xml index 4aeeecb..b8fdbc1 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -16,7 +16,7 @@ Supported design token sets: Configure which token set to use in the admin settings. ]]> - 0.1.1-unstable.2 + 0.1.1-unstable.4 agpl Conduction NLDesign diff --git a/appinfo/routes.php b/appinfo/routes.php index 54232a0..87ce0cc 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -6,5 +6,7 @@ 'routes' => [ ['name' => 'settings#setTokenSet', 'url' => '/settings/tokenset', 'verb' => 'POST'], ['name' => 'settings#getTokenSet', 'url' => '/settings/tokenset', 'verb' => 'GET'], + ['name' => 'settings#setSloganSetting', 'url' => '/settings/slogan', 'verb' => 'POST'], + ['name' => 'settings#setMenuLabelsSetting', 'url' => '/settings/menulabels', 'verb' => 'POST'], ], ]; diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..3da8d67 --- /dev/null +++ b/composer.lock @@ -0,0 +1,501 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "4c1f12c66945c04972b5f6f99b70a032", + "packages": [], + "packages-dev": [ + { + "name": "kubawerlos/php-cs-fixer-custom-fixers", + "version": "v3.36.0", + "source": { + "type": "git", + "url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git", + "reference": "e1f97f6463f0b2a22e0dd320948a04132ff9c501" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/e1f97f6463f0b2a22e0dd320948a04132ff9c501", + "reference": "e1f97f6463f0b2a22e0dd320948a04132ff9c501", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "ext-tokenizer": "*", + "friendsofphp/php-cs-fixer": "^3.87", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6.24 || ^10.5.51 || ^11.5.44" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpCsFixerCustomFixers\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kuba WerΕ‚os", + "email": "werlos@gmail.com" + } + ], + "description": "A set of custom fixers for PHP CS Fixer", + "support": { + "issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues", + "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.36.0" + }, + "funding": [ + { + "url": "https://github.com/kubawerlos", + "type": "github" + } + ], + "time": "2026-01-31T07:02:11+00:00" + }, + { + "name": "nextcloud/coding-standard", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/nextcloud/coding-standard.git", + "reference": "8e06808c1423e9208d63d1bd205b9a38bd400011" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/8e06808c1423e9208d63d1bd205b9a38bd400011", + "reference": "8e06808c1423e9208d63d1bd205b9a38bd400011", + "shasum": "" + }, + "require": { + "kubawerlos/php-cs-fixer-custom-fixers": "^3.22", + "php": "^8.0", + "php-cs-fixer/shim": "^3.17" + }, + "type": "library", + "autoload": { + "psr-4": { + "Nextcloud\\CodingStandard\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christoph Wurst", + "email": "christoph@winzerhof-wurst.at" + } + ], + "description": "Nextcloud coding standards for the php cs fixer", + "keywords": [ + "dev" + ], + "support": { + "issues": "https://github.com/nextcloud/coding-standard/issues", + "source": "https://github.com/nextcloud/coding-standard/tree/v1.4.0" + }, + "time": "2025-06-19T12:27:27+00:00" + }, + { + "name": "nextcloud/ocp", + "version": "v31.0.9", + "source": { + "type": "git", + "url": "https://github.com/nextcloud-deps/ocp.git", + "reference": "abd32429d794ede1d92b7b0a88a1070371c907b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/abd32429d794ede1d92b7b0a88a1070371c907b5", + "reference": "abd32429d794ede1d92b7b0a88a1070371c907b5", + "shasum": "" + }, + "require": { + "php": "~8.1 || ~8.2 || ~8.3 || ~8.4", + "psr/clock": "^1.0", + "psr/container": "^2.0.2", + "psr/event-dispatcher": "^1.0", + "psr/log": "^3.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-stable31": "31.0.0-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Christoph Wurst", + "email": "christoph@winzerhof-wurst.at" + }, + { + "name": "Joas Schilling", + "email": "coding@schilljs.com" + } + ], + "description": "Composer package containing Nextcloud's public OCP API and the unstable NCU API", + "support": { + "issues": "https://github.com/nextcloud-deps/ocp/issues", + "source": "https://github.com/nextcloud-deps/ocp/tree/v31.0.9" + }, + "time": "2025-07-31T00:57:37+00:00" + }, + { + "name": "php-cs-fixer/shim", + "version": "v3.93.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/shim.git", + "reference": "3a9db22e8f01762fddd3a85b998053294c5a3629" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/3a9db22e8f01762fddd3a85b998053294c5a3629", + "reference": "3a9db22e8f01762fddd3a85b998053294c5a3629", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "replace": { + "friendsofphp/php-cs-fixer": "self.version" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer", + "php-cs-fixer.phar" + ], + "type": "application", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz RumiΕ„ski", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/PHP-CS-Fixer/shim/issues", + "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.93.1" + }, + "time": "2026-01-28T23:51:14+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.13.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-11-04T16:30:35+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^8.1" + }, + "platform-dev": [], + "platform-overrides": { + "php": "8.1" + }, + "plugin-api-version": "2.6.0" +} diff --git a/css/admin.css b/css/admin.css index b953487..2f8ef22 100644 --- a/css/admin.css +++ b/css/admin.css @@ -63,6 +63,23 @@ font-size: 0.9em; } +/* Options section */ +.nldesign-option { + display: flex; + align-items: center; + gap: 12px; + padding: 16px; + border: 1px solid var(--color-border); + border-radius: var(--border-radius-large); + background: var(--color-main-background); + margin-bottom: 1em; +} + +.nldesign-option label { + cursor: pointer; + flex: 1; +} + /* Preview section */ .nldesign-preview { margin-top: 2em; diff --git a/css/fonts.css b/css/fonts.css index e074ef7..c0b64fa 100644 --- a/css/fonts.css +++ b/css/fonts.css @@ -5,18 +5,11 @@ * from @rijkshuisstijl-community/font package */ -/* Import Fira Sans from fontsource */ -@import url('~@fontsource/fira-sans/400.css'); -@import url('~@fontsource/fira-sans/400-italic.css'); -@import url('~@fontsource/fira-sans/700.css'); -@import url('~@fontsource/fira-sans/700-italic.css'); - -/* Fallback for direct file access */ @font-face { font-family: 'Fira Sans'; src: local('Fira Sans'), - url('../fonts/fira-sans-400-normal.woff2') format('woff2'), - url('../fonts/fira-sans-400-normal.woff') format('woff'); + url('fonts/fira-sans-latin-400-normal.woff2') format('woff2'), + url('fonts/fira-sans-latin-400-normal.woff') format('woff'); font-weight: 400; font-style: normal; font-display: swap; @@ -25,8 +18,8 @@ @font-face { font-family: 'Fira Sans'; src: local('Fira Sans Italic'), - url('../fonts/fira-sans-400-italic.woff2') format('woff2'), - url('../fonts/fira-sans-400-italic.woff') format('woff'); + url('fonts/fira-sans-latin-400-italic.woff2') format('woff2'), + url('fonts/fira-sans-latin-400-italic.woff') format('woff'); font-weight: 400; font-style: italic; font-display: swap; @@ -35,8 +28,8 @@ @font-face { font-family: 'Fira Sans'; src: local('Fira Sans Bold'), - url('../fonts/fira-sans-700-normal.woff2') format('woff2'), - url('../fonts/fira-sans-700-normal.woff') format('woff'); + url('fonts/fira-sans-latin-700-normal.woff2') format('woff2'), + url('fonts/fira-sans-latin-700-normal.woff') format('woff'); font-weight: 700; font-style: normal; font-display: swap; @@ -45,8 +38,8 @@ @font-face { font-family: 'Fira Sans'; src: local('Fira Sans Bold Italic'), - url('../fonts/fira-sans-700-italic.woff2') format('woff2'), - url('../fonts/fira-sans-700-italic.woff') format('woff'); + url('fonts/fira-sans-latin-700-italic.woff2') format('woff2'), + url('fonts/fira-sans-latin-700-italic.woff') format('woff'); font-weight: 700; font-style: italic; font-display: swap; diff --git a/css/hide-slogan.css b/css/hide-slogan.css new file mode 100644 index 0000000..cea4e16 --- /dev/null +++ b/css/hide-slogan.css @@ -0,0 +1,16 @@ +/** + * Hide Nextcloud slogan/payoff on login page + * + * This CSS file is loaded when the admin has enabled the "Hide slogan" setting + * in the NL Design System Theme admin panel. + */ + +/* Hide the footer.guest-box that contains the payoff/slogan */ +footer.guest-box, +#body-login footer.guest-box, +body.body-login-container footer.guest-box { + display: none !important; + visibility: hidden !important; +} + + diff --git a/css/logo-amsterdam.css b/css/logo-amsterdam.css deleted file mode 100644 index f60685f..0000000 --- a/css/logo-amsterdam.css +++ /dev/null @@ -1,18 +0,0 @@ -/** - * NL Design System - Amsterdam Logo - * - * Coat of arms for the municipality of Amsterdam - */ - -/* Amsterdam coat of arms */ -#nextcloud::before { - content: '' !important; - display: block !important; - width: 32px !important; - height: 40px !important; - background-image: url('../img/amsterdam-logo.svg') !important; - background-size: contain !important; - background-repeat: no-repeat !important; - background-position: center !important; - text-indent: 0 !important; -} diff --git a/css/logo-denhaag.css b/css/logo-denhaag.css deleted file mode 100644 index 50bcf3f..0000000 --- a/css/logo-denhaag.css +++ /dev/null @@ -1,18 +0,0 @@ -/** - * NL Design System - Den Haag Logo - * - * Coat of arms for the municipality of Den Haag (The Hague) - */ - -/* Den Haag coat of arms */ -#nextcloud::before { - content: '' !important; - display: block !important; - width: 34px !important; - height: 40px !important; - background-image: url('../img/denhaag-logo.svg') !important; - background-size: contain !important; - background-repeat: no-repeat !important; - background-position: center !important; - text-indent: 0 !important; -} diff --git a/css/logo-rijkshuisstijl.css b/css/logo-rijkshuisstijl.css deleted file mode 100644 index b94ed4d..0000000 --- a/css/logo-rijkshuisstijl.css +++ /dev/null @@ -1,18 +0,0 @@ -/** - * NL Design System - Rijkshuisstijl Logo - * - * Logo for the national government (Nederland map) - */ - -/* Nederland map icon (larger, centered, proper aspect ratio) */ -#nextcloud::before { - content: '' !important; - display: block !important; - width: 28px !important; - height: 36px !important; - background-image: url('../img/nederland-map.svg') !important; - background-size: contain !important; - background-repeat: no-repeat !important; - background-position: center !important; - text-indent: 0 !important; -} diff --git a/css/logo-rotterdam.css b/css/logo-rotterdam.css deleted file mode 100644 index f1c1863..0000000 --- a/css/logo-rotterdam.css +++ /dev/null @@ -1,18 +0,0 @@ -/** - * NL Design System - Rotterdam Logo - * - * Coat of arms for the municipality of Rotterdam - */ - -/* Rotterdam coat of arms */ -#nextcloud::before { - content: '' !important; - display: block !important; - width: 32px !important; - height: 36px !important; - background-image: url('../img/rotterdam-logo.svg') !important; - background-size: contain !important; - background-repeat: no-repeat !important; - background-position: center !important; - text-indent: 0 !important; -} diff --git a/css/logo-utrecht.css b/css/logo-utrecht.css deleted file mode 100644 index 0bbbf8a..0000000 --- a/css/logo-utrecht.css +++ /dev/null @@ -1,18 +0,0 @@ -/** - * NL Design System - Utrecht Logo - * - * Coat of arms for the municipality of Utrecht - */ - -/* Utrecht coat of arms */ -#nextcloud::before { - content: '' !important; - display: block !important; - width: 36px !important; - height: 36px !important; - background-image: url('../img/utrecht-logo.svg') !important; - background-size: contain !important; - background-repeat: no-repeat !important; - background-position: center !important; - text-indent: 0 !important; -} diff --git a/css/nuclear.css b/css/nuclear.css deleted file mode 100644 index e34bb3f..0000000 --- a/css/nuclear.css +++ /dev/null @@ -1,17 +0,0 @@ -/** - * GRADIENT HANDLING - DOCUMENTED LIMITATION - * - * Nextcloud core uses inline CSS gradients (background-image: linear-gradient()) - * combined with -webkit-mask-image for most navigation icons. - * - * These inline styles have the highest CSS specificity and cannot be overridden - * with external stylesheets. This is a Nextcloud core limitation. - * - * DECISION: Accept gradient icons as-is to maintain stability and functionality. - * - * Note: SVG-based icons (like some app icons) can have gradients removed, - * but this affects a minority of icons and isn't worth the trade-offs. - */ - -/* This file intentionally left minimal to document the limitation. */ -/* No aggressive overrides that break layout or functionality. */ diff --git a/css/overrides.css b/css/overrides.css index 8abbdd7..88bdf71 100644 --- a/css/overrides.css +++ b/css/overrides.css @@ -34,6 +34,175 @@ ol { font-family: 'Fira Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Cantarell, Ubuntu, 'Helvetica Neue', Arial, sans-serif !important; } +/* ============================================ + OVERRIDE NEXTCLOUD CONTAINER VARIABLES + ============================================ */ + +/* Override body container radius to use NL Design border radius. */ +:root, +body { + --body-container-radius: var(--nldesign-border-radius) !important; + --body-container-margin: 15px !important; +} + +/* NL Design Layout - Separate navigation and content cards. */ +/* Main content wrapper should be transparent to show background. */ +#content, +.content[data-v-1f87d811] { + background: transparent !important; + background-color: transparent !important; +} + +/* App navigation (left sidebar) as white card with margin. */ +#app-navigation, +.app-navigation, +#app-navigation-vue { + background: var(--color-main-background) !important; + background-color: var(--color-main-background) !important; + margin-right: 30px !important; + border-radius: var(--nldesign-border-radius) !important; +} + +/* Remove margin when navigation is closed. */ +#app-navigation.app-navigation--close, +.app-navigation.app-navigation--close { + margin-right: 0 !important; +} + +/* Navigation toggle button - remove excessive padding. */ +.app-navigation-toggle, +.app-navigation-toggle.button-vue, +button.app-navigation-toggle { + padding: 0 !important; + padding-inline: 0 !important; + padding-block: 0 !important; + min-width: var(--default-clickable-area) !important; + min-height: var(--default-clickable-area) !important; +} + +/* App content (main area) as white card. */ +#app-content, +.app-content, +#app-content-vue, +.app-content-wrapper { + background: var(--color-main-background) !important; + background-color: var(--color-main-background) !important; + border-radius: var(--nldesign-border-radius) !important; +} + +/* MyDash specific styling - transparent background and no padding/margins. */ +/* Target the NcAppContent component wrapper for MyDash. */ +body #content #app-content-vue.app-content, +body #content main.app-content, +body #app-mydash .app-content, +body #app-mydash #app-content-vue, +body #app-mydash main.app-content, +body [data-v-e64fb40a][data-v-1f87d811].app-content, +body main[data-v-e64fb40a].app-content, +#mydash-app { + background: transparent !important; + background-color: transparent !important; + margin: 0 !important; + padding: 0 !important; +} + +/* MyDash content wrapper - special margins. */ +#content.app-mydash { + margin-left: 2px !important; + margin-right: 0 !important; + margin-bottom: 0 !important; + margin-top: 40px !important; +} + +/* Even more specific - target by parent app structure. */ +#content-vue[data-app="mydash"] .app-content, +div[id*="mydash"] ~ .app-content, +body:has(#mydash-app) #app-content-vue, +body:has(#mydash-app) main.app-content { + background: transparent !important; + background-color: transparent !important; +} + +.mydash-container { + padding: 0 !important; + margin: 0 !important; +} + +/* ============================================ + FIX HEADER-END SPACING + ============================================ */ + +/* Add padding to the right side of the header end section and remove margin. */ +.header-end, +#header .header-end, +header#header .header-end { + padding-right: 15px !important; + margin: 0 !important; +} + +/* Add right positioning to header menu trigger. */ +.header-menu__trigger, +#header .header-menu__trigger, +header#header .header-menu__trigger { + right: 12px !important; +} + +/* ============================================ + FIX HEADER-END ICONS (make them visible on white background) + ============================================ */ + +/* Invert icons in header-end section to make them dark on white background. */ +html body #header .header-end svg, +body #header .header-end svg, +#header .header-end svg, +html body #header .header-end .button-vue__icon, +body #header .header-end .button-vue__icon, +#header .header-end .button-vue__icon, +html body #header .header-end .icon-vue, +body #header .header-end .icon-vue, +#header .header-end .icon-vue, +html body #header .header-end img, +body #header .header-end img, +#header .header-end img { + filter: invert(1) brightness(0) contrast(100) !important; + -webkit-filter: invert(1) brightness(0) contrast(100) !important; + opacity: 1 !important; +} + +/* Exception: Don't invert the avatar image. */ +html body #header .header-end .avatardiv img, +body #header .header-end .avatardiv img, +#header .header-end .avatardiv img { + filter: none !important; + -webkit-filter: none !important; +} + +/* Exception: Don't invert the user status icon (it has its own color). */ +html body #header .header-end .user-status-icon svg, +body #header .header-end .user-status-icon svg, +#header .header-end .user-status-icon svg { + filter: none !important; + -webkit-filter: none !important; +} + +/* Remove gradient masks from header-end icons. */ +html body #header .header-end .icon-vue, +body #header .header-end .icon-vue, +#header .header-end .icon-vue, +html body #header .header-end .button-vue__icon, +body #header .header-end .button-vue__icon, +#header .header-end .button-vue__icon, +html body #header .header-end [class*="icon"], +body #header .header-end [class*="icon"], +#header .header-end [class*="icon"] { + mask: none !important; + -webkit-mask: none !important; + mask-image: none !important; + -webkit-mask-image: none !important; + background-image: none !important; + background: transparent !important; +} + /* ============================================ FIX "GOEDEMORGEN" VISIBILITY ============================================ */ @@ -99,9 +268,11 @@ header nav .icon { NEXTCLOUD LOGO REPLACEMENT ============================================ */ -/* Hide original logo image */ -#nextcloud .logo, -#nextcloud .logo-icon, +/* Note: Logo is now handled via normal Nextcloud theming. + Apps like mydash can style the logo container as needed. + We keep #nextcloud minimal and let the .logo.logo-icon show normally. */ + +/* Hide direct svg/img children (not the .logo-icon div) */ #nextcloud > svg, #nextcloud > img, #nextcloud > a > svg, @@ -110,25 +281,18 @@ header nav .icon { visibility: hidden !important; } -/* Hide any text content inside */ -#nextcloud, -#nextcloud a, -#nextcloud span, -#nextcloud div { - font-size: 0 !important; - color: transparent !important; - text-indent: -9999px !important; +/* Let .logo and .logo-icon be visible - they contain the themed logo */ +#nextcloud .logo, +#nextcloud .logo-icon { + /* Don't hide these - they contain the configured logo via background-image. */ } -/* Show only logo icon (specific icon loaded via separate CSS based on token set) */ +/* Basic nextcloud link styling */ #nextcloud { visibility: visible !important; display: flex !important; align-items: center !important; justify-content: center !important; - width: 50px !important; - height: 50px !important; - padding: 8px !important; } /* Logo icon styling is defined in logo-{tokenset}.css files */ @@ -138,12 +302,12 @@ header nav .icon { ============================================ */ /* Set default solid backgrounds, but allow custom widget styles to override */ -/* Exclude tiles and MyDash widgets to allow custom styling */ -.panel:not(.tile-widget):not(.tile-widget *):not(.mydash-widget):not(.mydash-widget *), -.widget:not(.tile-widget):not(.tile-widget *):not(.mydash-widget):not(.mydash-widget *), -.dashboard-widget:not(.tile-widget):not(.tile-widget *):not(.mydash-widget):not(.mydash-widget *), -[class*="panel"]:not(.tile-widget):not(.tile-widget *):not(.mydash-widget):not(.mydash-widget *), -[class*="widget"]:not(.tile-widget):not(.tile-widget *):not(.mydash-widget):not(.mydash-widget *), +/* Exclude tiles, MyDash widgets, and header elements to allow custom styling */ +.panel:not(.tile-widget):not(.tile-widget *):not(.mydash-widget):not(.mydash-widget *):not(#header *), +.widget:not(.tile-widget):not(.tile-widget *):not(.mydash-widget):not(.mydash-widget *):not(#header *), +.dashboard-widget:not(.tile-widget):not(.tile-widget *):not(.mydash-widget):not(.mydash-widget *):not(#header *), +[class*="panel"]:not(.tile-widget):not(.tile-widget *):not(.mydash-widget):not(.mydash-widget *):not(#header *), +[class*="widget"]:not(.tile-widget):not(.tile-widget *):not(.mydash-widget):not(.mydash-widget *):not(#header *), #app-content, .app-content { background: #ffffff; @@ -309,3 +473,479 @@ select, #expand:hover { border-radius: var(--nldesign-border-radius) !important; } + +/* ============================================ + LOGIN PAGE - RIJKSOVERHEID BLUE BAR + ============================================ + + Blue bar with logo at top center: + - Blue bar: 60px wide Γ— 90px high + - Logo: 50px wide, positioned at bottom with 5px margin + ============================================ */ + +/* Narrow blue bar at top center. */ +#body-login .guest-box.login-box::before, +#body-login div.guest-box.login-box::before, +div.guest-box.login-box::before { + content: '' !important; + position: absolute !important; + top: 0 !important; + left: 50% !important; + transform: translateX(-50%) !important; + width: 60px !important; + height: 90px !important; + background: #154273 !important; + z-index: 1000 !important; + display: block !important; + pointer-events: none !important; +} + +/* White logo in the narrow blue bar - positioned at bottom. */ +#body-login .guest-box.login-box::after, +#body-login div.guest-box.login-box::after, +div.guest-box.login-box::after { + content: '' !important; + position: absolute !important; + top: 35px !important; /* 90px - 50px logo - 5px margin = 35px from top */ + left: 50% !important; + transform: translateX(-50%) !important; + width: 50px !important; + height: 50px !important; + background-image: var(--image-logo, url('../img/nederland-logo.svg')) !important; + background-repeat: no-repeat !important; + background-position: center !important; + background-size: contain !important; + filter: brightness(0) invert(1) !important; + z-index: 1001 !important; + display: block !important; + pointer-events: none !important; +} + +/* Push guest-box content down. */ +#body-login .guest-box.login-box, +#body-login div.guest-box.login-box, +div.guest-box.login-box { + position: relative !important; + padding-top: 90px !important; + overflow: visible !important; +} + +/* ============================================ + LOGGED-IN HEADER STYLING (NL DESIGN) + ============================================ + + This section applies NL Design styling to the Nextcloud header: + - White header background + - Blue bar with logo at left (using configured Nextcloud logo) + - Dark text and icons on white background + - Token-based colors for consistency + ============================================ */ + +/* Remove any top margin/padding from body to eliminate white space. */ +html, +body { + margin-top: 0 !important; + padding-top: 0 !important; +} + +/* Allow header to overflow for blue bar sticking out. */ +html body.body-user #body-user, +body.body-user #body-user, +html body #body-user, +body #body-user, +#body-user { + overflow: visible !important; + margin-top: 0 !important; + padding-top: 0 !important; +} + +/* White header background. */ +html body.body-user #body-user #header, +body.body-user #body-user #header, +html body #body-user #header, +body #body-user #header, +html body #header, +body #header, +#body-user #header, +#header { + background-color: #ffffff !important; + background-image: none !important; + background: #ffffff !important; + border-bottom: var(--nldesign-header-border-bottom, 0) !important; + position: relative !important; + overflow: visible !important; +} + +/* Blue bar container - target the actual nextcloud logo link. */ +html body #header a#nextcloud, +body #header a#nextcloud, +#header a#nextcloud, +html body #header a[href="/"], +body #header a[href="/"], +#header a[href="/"] { + position: fixed !important; + left: 15px !important; + top: 0 !important; + width: 60px !important; + height: 90px !important; + margin: 0 !important; + background: #154273 !important; + background-color: #154273 !important; + display: flex !important; + flex-direction: column !important; + align-items: center !important; + justify-content: flex-end !important; + padding: 0 !important; + overflow: hidden !important; + box-sizing: border-box !important; + text-align: center !important; +} + +/* Disable pseudo-element logo approach. */ +html body #header a#nextcloud::before, +body #header a#nextcloud::before, +#header a#nextcloud::before, +html body #header a[href="/"]::before, +body #header a[href="/"]::before, +#header a[href="/"]::before { + content: none !important; + display: none !important; +} + +/* Logo element inside the blue bar - use the actual Nextcloud configured logo. */ +html body #header a#nextcloud .logo.logo-icon, +body #header a#nextcloud .logo.logo-icon, +#header a#nextcloud .logo.logo-icon, +html body #header a[href="/"] .logo.logo-icon, +body #header a[href="/"] .logo.logo-icon, +#header a[href="/"] .logo.logo-icon { + display: block !important; + visibility: visible !important; + width: 50px !important; + min-width: 50px !important; + max-width: 50px !important; + height: 0 !important; + min-height: 50px !important; + max-height: 65px !important; + background-size: contain !important; + background-repeat: no-repeat !important; + background-position: center center !important; + background-color: transparent !important; + background-image: var(--image-logoheader, var(--image-logo, url(../img/logo/logo.svg))) !important; + filter: brightness(0) invert(1) !important; + opacity: 1 !important; + margin: 0 5px 5px 5px !important; + padding: 0 !important; + flex-shrink: 0 !important; + flex-grow: 0 !important; + position: static !important; + text-indent: 0 !important; + overflow: visible !important; + border: none !important; + box-shadow: none !important; + font-size: 0 !important; +} + +/* Remove old pseudo-elements. */ +html body #header::before, +body #header::before, +#header::before, +html body #header::after, +body #header::after, +#header::after { + content: none !important; + display: none !important; +} + +/* Push header left content to make space for blue bar. */ +html body #header .header-left, +body #header .header-left, +#header .header-left, +html body #header .header-start, +body #header .header-start, +#header .header-start { + position: relative !important; + z-index: 1000 !important; + padding-left: 80px !important; +} + +/* App menu - remove padding since logo is in normal flow. */ +html body #header nav.app-menu, +body #header nav.app-menu, +#header nav.app-menu { + background: transparent !important; + padding-left: 0 !important; + margin-left: 0 !important; +} + +html body #header nav.app-menu ul, +body #header nav.app-menu ul, +#header nav.app-menu ul { + padding-left: 0 !important; + margin-left: 0 !important; +} + +html body #header nav.app-menu li, +body #header nav.app-menu li, +#header nav.app-menu li { + margin-left: 0 !important; +} + +/* App menu styling - use theme colors. */ +html body #header nav.app-menu a, +body #header nav.app-menu a, +#header nav.app-menu a, +html body #header nav.app-menu .app-menu-entry__link, +body #header nav.app-menu .app-menu-entry__link, +#header nav.app-menu .app-menu-entry__link { + color: var(--color-main-text) !important; + font-weight: 400 !important; +} + +/* App menu text labels - explicitly set dark color. */ +html body #header nav.app-menu .app-menu-entry__label, +body #header nav.app-menu .app-menu-entry__label, +#header nav.app-menu .app-menu-entry__label, +html body #header nav.app-menu .app-menu-entry .app-menu-entry__label, +body #header nav.app-menu .app-menu-entry .app-menu-entry__label, +#header nav.app-menu .app-menu-entry .app-menu-entry__label { + color: var(--color-main-text) !important; + font-weight: 400 !important; +} + +/* App menu icons - invert if SVGs are white to make them dark. */ +/* CRITICAL: If SVGs are white, invert them to dark for white header background. */ +/* Maximum specificity to override #header .app-menu-icon from theme.css */ +html body #header nav.app-menu .app-menu-entry .app-menu-icon img.app-menu-icon__icon, +html body #header nav.app-menu .app-menu-entry .app-menu-icon img, +html body #header nav.app-menu .app-menu-icon img, +body #header nav.app-menu .app-menu-icon img, +#header nav.app-menu .app-menu-icon img, +html body #header nav.app-menu .app-menu-icon__icon, +body #header nav.app-menu .app-menu-icon__icon, +#header nav.app-menu .app-menu-icon__icon, +html body #header nav.app-menu .app-menu-entry .app-menu-icon, +html body #header nav.app-menu .app-menu-icon, +body #header nav.app-menu .app-menu-icon, +#header nav.app-menu .app-menu-icon, +html body #header nav.app-menu .app-menu-entry__icon, +body #header nav.app-menu .app-menu-entry__icon, +#header nav.app-menu .app-menu-entry__icon, +html body #header nav.app-menu .app-menu-entry__icon img, +body #header nav.app-menu .app-menu-entry__icon img, +#header nav.app-menu .app-menu-entry__icon img, +html body #header nav.app-menu img, +body #header nav.app-menu img, +#header nav.app-menu img { + filter: invert(1) brightness(0) contrast(100) !important; + -webkit-filter: invert(1) brightness(0) contrast(100) !important; + opacity: 1 !important; +} + +/* App menu SVGs should also be inverted. */ +html body #header nav.app-menu svg, +body #header nav.app-menu svg, +#header nav.app-menu svg, +html body #header nav.app-menu .app-menu-icon svg, +body #header nav.app-menu .app-menu-icon svg, +#header nav.app-menu .app-menu-icon svg { + filter: invert(1) !important; + -webkit-filter: invert(1) !important; +} + +html body #header nav.app-menu svg, +body #header nav.app-menu svg, +#header nav.app-menu svg, +html body #header nav.app-menu .app-menu-icon svg, +body #header nav.app-menu .app-menu-icon svg, +#header nav.app-menu .app-menu-icon svg { + filter: none !important; + -webkit-filter: none !important; +} + +/* Active and hover app menu items. */ +html body #header nav.app-menu .app-menu-entry--active a, +body #header nav.app-menu .app-menu-entry--active a, +#header nav.app-menu .app-menu-entry--active a, +html body #header nav.app-menu .app-menu-entry--active .app-menu-entry__link, +body #header nav.app-menu .app-menu-entry--active .app-menu-entry__link, +#header nav.app-menu .app-menu-entry--active .app-menu-entry__link { + color: var(--color-primary) !important; + font-weight: 600 !important; + position: relative !important; +} + +/* Blue indicator bar at bottom of active menu item using box-shadow. */ +html body #header nav.app-menu .app-menu-entry--active a::after, +body #header nav.app-menu .app-menu-entry--active a::after, +#header nav.app-menu .app-menu-entry--active a::after, +html body #header nav.app-menu .app-menu-entry--active .app-menu-entry__link::after, +body #header nav.app-menu .app-menu-entry--active .app-menu-entry__link::after, +#header nav.app-menu .app-menu-entry--active .app-menu-entry__link::after { + content: '' !important; + position: absolute !important; + bottom: 0 !important; + left: 0 !important; + right: 0 !important; + height: 5px !important; + background: var(--color-primary) !important; + display: block !important; +} + +/* Ensure all menu entries have consistent bottom spacing. */ +html body #header nav.app-menu .app-menu-entry, +body #header nav.app-menu .app-menu-entry, +#header nav.app-menu .app-menu-entry, +html body #header nav.app-menu .app-menu-entry a, +body #header nav.app-menu .app-menu-entry a, +#header nav.app-menu .app-menu-entry a, +html body #header nav.app-menu .app-menu-entry .app-menu-entry__link, +body #header nav.app-menu .app-menu-entry .app-menu-entry__link, +#header nav.app-menu .app-menu-entry .app-menu-entry__link { + margin: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +/* Active app menu text labels. */ +html body #header nav.app-menu .app-menu-entry--active .app-menu-entry__label, +body #header nav.app-menu .app-menu-entry--active .app-menu-entry__label, +#header nav.app-menu .app-menu-entry--active .app-menu-entry__label { + color: var(--color-primary) !important; + font-weight: 600 !important; +} + +html body #header nav.app-menu a:hover, +body #header nav.app-menu a:hover, +#header nav.app-menu a:hover, +html body #header nav.app-menu .app-menu-entry__link:hover, +body #header nav.app-menu .app-menu-entry__link:hover, +#header nav.app-menu .app-menu-entry__link:hover { + background-color: var(--color-background-hover) !important; + color: var(--color-primary) !important; +} + +/* Hover app menu text labels. */ +html body #header nav.app-menu .app-menu-entry__link:hover .app-menu-entry__label, +body #header nav.app-menu .app-menu-entry__link:hover .app-menu-entry__label, +#header nav.app-menu .app-menu-entry__link:hover .app-menu-entry__label { + color: var(--color-primary) !important; +} + +/* Header app name and branding text. */ +html body #header .header-appname, +body #header .header-appname, +#header .header-appname, +html body #header .header-left a, +body #header .header-left a, +#header .header-left a { + color: #154273 !important; + font-weight: 600 !important; +} + +/* Header right side icons and buttons. */ +html body #header .header-right a, +body #header .header-right a, +#header .header-right a, +html body #header .header-right button, +body #header .header-right button, +#header .header-right button, +html body #header .menutoggle, +body #header .menutoggle, +#header .menutoggle { + color: #333333 !important; + opacity: 1 !important; +} + +html body #header .header-right [class^="icon-"], +body #header .header-right [class^="icon-"], +#header .header-right [class^="icon-"], +html body #header .header-right svg, +body #header .header-right svg, +#header .header-right svg { + filter: none !important; + opacity: 0.8 !important; +} + +/* Unified search. */ +html body #header .unified-search__button, +body #header .unified-search__button, +#header .unified-search__button { + color: #333333 !important; + filter: none !important; +} + +/* Header dropdown panels and menus - ensure proper colors. */ +html body #header .header-menu, +body #header .header-menu, +#header .header-menu, +html body #header #user-menu, +body #header #user-menu, +#header #user-menu, +html body #header .popover, +body #header .popover, +#header .popover { + background-color: var(--color-main-background) !important; + color: var(--color-main-text) !important; +} + +html body #header .unified-search__button svg, +body #header .unified-search__button svg, +#header .unified-search__button svg { + fill: #333333 !important; + filter: none !important; +} + +/* User menu and notifications. */ +html body #header .header-right .icon-more, +body #header .header-right .icon-more, +#header .header-right .icon-more, +html body #header .header-right .notifications, +body #header .header-right .notifications, +#header .header-right .notifications, +html body #header .header-right .contactsmenu, +body #header .header-right .contactsmenu, +#header .header-right .contactsmenu { + opacity: 0.8 !important; +} + +html body #header .header-right #settings, +body #header .header-right #settings, +#header .header-right #settings { + filter: none !important; + opacity: 0.8 !important; +} + +/* Legacy #appmenu support. */ +html body #header #appmenu, +body #header #appmenu, +#header #appmenu { + background: transparent !important; + padding-left: 60px !important; + margin-left: 0 !important; +} + +html body #header #appmenu li a, +body #header #appmenu li a, +#header #appmenu li a { + color: var(--color-main-text) !important; + font-weight: 400 !important; +} + +html body #header #appmenu li.active a, +body #header #appmenu li.active a, +#header #appmenu li.active a, +html body #header #appmenu li:hover a, +body #header #appmenu li:hover a, +#header #appmenu li:hover a { + background-color: var(--color-background-hover) !important; + color: var(--color-primary) !important; + font-weight: 600 !important; +} + +html body #header .header-appname-container, +body #header .header-appname-container, +#header .header-appname-container { + position: relative !important; + z-index: auto !important; +} diff --git a/css/show-menu-labels.css b/css/show-menu-labels.css new file mode 100644 index 0000000..dd455a0 --- /dev/null +++ b/css/show-menu-labels.css @@ -0,0 +1,65 @@ +/** + * SPDX-FileCopyrightText: 2024 NLDesign Contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * Show Menu Labels Mode + * Shows app names instead of icons in the header menu + */ + +/* Hide app menu icons. */ +#header nav.app-menu .app-menu-icon, +#header nav.app-menu .app-menu-entry__icon { + display: none !important; + visibility: hidden !important; +} + +/* Make labels always visible and properly styled. */ +#header nav.app-menu .app-menu-entry__label { + display: inline-block !important; + visibility: visible !important; + opacity: 1 !important; + font-size: 14px !important; + font-weight: 400 !important; + padding: 0 8px !important; + line-height: 1.4 !important; + white-space: nowrap !important; + text-align: center !important; + vertical-align: middle !important; + position: static !important; + top: auto !important; + bottom: auto !important; + left: auto !important; + right: auto !important; + transform: none !important; + max-width: none !important; +} + +/* Active label styling. */ +#header nav.app-menu .app-menu-entry--active .app-menu-entry__label { + font-weight: 600 !important; +} + +/* Disable Nextcloud's default active indicator (black dot from AppMenuEntry.vue). */ +#header nav.app-menu .app-menu-entry--active::before { + background-color: transparent !important; + opacity: 0 !important; +} + +/* Menu entry link should be full height and center content. */ +#header nav.app-menu .app-menu-entry__link { + height: 100% !important; + display: flex !important; + flex-direction: column !important; + align-items: center !important; + justify-content: center !important; + padding: 0 !important; +} + +/* Menu entry item full height. */ +#header nav.app-menu .app-menu-entry { + height: var(--header-height) !important; + min-width: 80px !important; + width: auto !important; + flex-shrink: 0 !important; +} + diff --git a/css/theme.css b/css/theme.css index 2b5f463..9738aba 100644 --- a/css/theme.css +++ b/css/theme.css @@ -20,8 +20,8 @@ body { --color-primary-element-light-text: var(--nldesign-color-primary) !important; --color-primary-element-light-hover: var(--nldesign-color-primary-light-hover) !important; - /* Background colors */ - --color-main-background: var(--nldesign-color-background) !important; + /* Background colors - use Nextcloud's own theming system. */ + /* --color-main-background: Managed by Nextcloud theming */ --color-background-hover: var(--nldesign-color-background-hover) !important; --color-background-dark: var(--nldesign-color-background-dark) !important; --color-background-darker: var(--nldesign-color-background-darker) !important; @@ -82,17 +82,6 @@ header#header { height: 0 !important; } -/* Add "Rijksoverheid" or organization name instead of logo */ -#nextcloud::after { - content: "Rijksoverheid" !important; - color: var(--nldesign-color-header-text) !important; - font-family: var(--nldesign-font-family) !important; - font-size: 20px !important; - font-weight: 700 !important; - line-height: 50px !important; - padding-left: 12px !important; -} - /* Header text and icons */ #header *, #header .header-appname, @@ -117,14 +106,156 @@ header#header { LOGIN PAGE ============================================ */ -/* Remove background image - Rijkshuisstijl uses solid colors */ +/* Light gray background like Rijkshuisstijl - use Nextcloud's theming. */ #body-login, .body-login-container { background-image: none !important; - background-color: var(--nldesign-color-background) !important; } -/* Login box primary button */ +/* Login wrapper - centered, clean design */ +#body-login .wrapper { + max-width: 500px; + margin: 0 auto; + box-shadow: none !important; + border-radius: 0 !important; + padding-top: 0 !important; /* Remove top padding so header is at top of box */ +} + +/* Main body-login container positioning */ +#body-login { + display: flex !important; + flex-direction: column !important; + align-items: center !important; + justify-content: center !important; + min-height: 100vh !important; +} + +/* Login form container - white background, no shadows, sharp corners */ +#body-login form, +#body-login .login-form, +#body-login #login-form, +#body-login .body-login-container > div { + background: #ffffff !important; + box-shadow: none !important; + border-radius: 0 !important; + border: none !important; +} + +/* Guest box and login box - clean Rijkshuisstijl style */ +#body-login .guest-box, +#body-login .login-box, +.guest-box, +.login-box { + background: #ffffff !important; + background-color: #ffffff !important; + box-shadow: none !important; + border-radius: 0 !important; + border: none !important; + backdrop-filter: none !important; + -webkit-backdrop-filter: none !important; + padding: 0 !important; /* Remove padding so header can be full width */ + overflow: hidden !important; /* Contain the header */ + position: relative !important; /* For pseudo-element positioning */ +} + +/* LOGIN PAGE HEADER - Rijksoverheid style with blue bar and logo */ +/* Remove the margin, make blue bar part of the login box */ +#body-login .guest-box.login-box, +#body-login .login-box, +#body-login div.guest-box, +#body-login div[class*="guest-box"], +#body-login div[class*="login-box"], +.guest-box.login-box, +div.guest-box.login-box, +div[data-v-48234338].guest-box { + position: relative !important; + overflow: hidden !important; /* Clip the pseudo-element to box width */ + margin-top: 0 !important; /* No extra margin needed */ + padding-top: 0 !important; +} + +/* Create blue bar at TOP of login box (not above it) */ +#body-login .guest-box.login-box::before, +#body-login div.guest-box::before, +#body-login div[class*="guest-box"]::before, +.guest-box.login-box::before, +div.guest-box.login-box::before, +div[data-v-48234338].guest-box::before { + content: '' !important; + position: absolute !important; + top: 0 !important; /* At the TOP of the login box */ + left: 0 !important; + right: 0 !important; + width: 100% !important; /* Full width of login box */ + height: 100px !important; /* Blue bar height */ + background: var(--nldesign-color-button-primary-background) !important; + background-color: var(--nldesign-color-button-primary-background) !important; + z-index: 1 !important; + display: block !important; + box-sizing: border-box !important; +} + +/* White logo in the blue bar */ +#body-login .guest-box.login-box::after, +#body-login div.guest-box::after, +.guest-box.login-box::after, +div.guest-box.login-box::after { + content: '' !important; + position: absolute !important; + top: 30px !important; /* Vertically centered in blue bar */ + left: 50% !important; + transform: translateX(-50%) !important; + width: 100px !important; + height: 40px !important; + background-image: url('../img/nederland-logo.svg') !important; + background-repeat: no-repeat !important; + background-position: center !important; + background-size: contain !important; + filter: brightness(0) invert(1) !important; + z-index: 2 !important; + display: block !important; +} + +/* Push login content below blue bar */ +#body-login div[class*="login-box__wrapper"], +#body-login .guest-box div[class*="login-box__wrapper"], +div[class*="login-box__wrapper"], +div[data-v-48234338][class*="login-box__wrapper"], +.login-box__wrapper[data-v-48234338], +[data-v-48234338].login-box__wrapper { + position: relative !important; + background: #ffffff !important; + padding-top: 100px !important; /* Space for blue bar */ + z-index: 3 !important; +} + +/* Form content - normal padding */ +#body-login div[class*="login-box__wrapper"] > form, +#body-login div[class*="login-box__wrapper"] > div, +div[class*="login-box__wrapper"] > form, +div[class*="login-box__wrapper"] > div { + padding-top: 12px !important; + background: transparent !important; +} + +/* Add padding back to the form content area */ +#body-login .guest-box form, +#body-login .login-box form, +#body-login .guest-box > div:not(#header), +#body-login .login-box > div:not(#header) { + padding: 12px !important; +} + +/* Remove all shadows from login elements */ +#body-login input, +#body-login button, +#body-login .button-vue, +#body-login a { + box-shadow: none !important; + border-radius: 0 !important; +} + +/* Login box primary button - sharp corners */ #body-login .button-vue--vue-primary, #body-login input[type="submit"], #body-login button.primary, @@ -132,19 +263,47 @@ header#header { background-color: var(--nldesign-color-button-primary-background) !important; border-color: var(--nldesign-color-button-primary-border) !important; color: var(--nldesign-color-button-primary-text) !important; - border-radius: var(--nldesign-border-radius) !important; + border-radius: 0 !important; + box-shadow: none !important; } #body-login .button-vue--vue-primary:hover, #body-login input[type="submit"]:hover, #body-login button.primary:hover { background-color: var(--nldesign-color-button-primary-hover) !important; + box-shadow: none !important; } -/* Login header/logo area */ +/* Login header/logo area - HIDE the original header that's outside the login box */ #body-login #header, -body.body-login-container #header { - background: var(--nldesign-color-header-background) !important; +#body-login header, +body.body-login-container #header, +body.body-login-container header { + display: none !important; +} + +/* Login content wrapper */ +#body-login .wrapper, +#body-login > .wrapper { + padding: 0 !important; + margin-top: 0 !important; +} + +/* Login form inputs - sharp corners */ +#body-login input[type="text"], +#body-login input[type="password"], +#body-login input[type="email"], +#body-login .input-field { + border-radius: 0 !important; + box-shadow: none !important; + border: 1px solid #d0d0d0 !important; +} + +#body-login input[type="text"]:focus, +#body-login input[type="password"]:focus, +#body-login input[type="email"]:focus { + border-color: var(--nldesign-color-button-primary-background) !important; + box-shadow: none !important; } /* ============================================ @@ -282,15 +441,13 @@ body, DASHBOARD - Remove gradients, backgrounds, transparency ============================================ */ -/* Remove all background images and gradients */ +/* Remove all background images and gradients - use Nextcloud theming for background. */ #body-user, #content, .app-dashboard, #app-content, #app-content-wrapper { background-image: none !important; - background: var(--nldesign-color-background) !important; - background-color: var(--nldesign-color-background) !important; } /* Fix "Goedemorgen" text visibility - ensure it's the right color */ @@ -302,16 +459,15 @@ h2, background-color: transparent !important; } -/* Solid widget backgrounds - no transparency */ -/* Exclude MyDash widgets and tiles to allow custom styling */ +/* Solid widget backgrounds - no transparency. */ +/* Exclude MyDash widgets and tiles to allow custom styling. */ +/* Use Nextcloud theming for background color. */ .panel:not(.mydash-widget):not(.mydash-widget *):not(.tile-widget):not(.tile-widget *), .panel--wrapper:not(.mydash-widget):not(.mydash-widget *):not(.tile-widget):not(.tile-widget *), .widget:not(.mydash-widget):not(.mydash-widget *):not(.tile-widget):not(.tile-widget *), .panel__content:not(.mydash-widget):not(.mydash-widget *):not(.tile-widget):not(.tile-widget *), .dashboard-widget:not(.mydash-widget):not(.mydash-widget *):not(.tile-widget):not(.tile-widget *), .dashboard-panel:not(.mydash-widget):not(.mydash-widget *):not(.tile-widget):not(.tile-widget *) { - background: var(--nldesign-color-background); - background-color: var(--nldesign-color-background); opacity: 1; backdrop-filter: none; } @@ -458,13 +614,11 @@ body *, -webkit-backdrop-filter: none !important; } -/* Solid backgrounds everywhere */ +/* Solid backgrounds everywhere - use Nextcloud theming. */ #app-content, .app-content-wrapper, .app-content-list, .app-content-detail { - background: var(--nldesign-color-background) !important; - background-color: var(--nldesign-color-background) !important; background-image: none !important; } diff --git a/css/tokens/amsterdam.css b/css/tokens/amsterdam.css index 4c38709..2545568 100644 --- a/css/tokens/amsterdam.css +++ b/css/tokens/amsterdam.css @@ -25,9 +25,8 @@ --amsterdam-color-blue: #004699; --amsterdam-color-purple: #a00078; - /* Background colors */ - --nldesign-color-background: #ffffff; - --nldesign-color-background-rgb: 255, 255, 255; + /* Background colors - for hover states and dark variants. */ + /* Main background managed by Nextcloud theming system. */ --nldesign-color-background-hover: #f5f5f5; --nldesign-color-background-dark: #e6e6e6; --nldesign-color-background-darker: #cccccc; diff --git a/css/tokens/denhaag.css b/css/tokens/denhaag.css index 6227974..2658909 100644 --- a/css/tokens/denhaag.css +++ b/css/tokens/denhaag.css @@ -21,9 +21,8 @@ --denhaag-color-red: #d52d2d; --denhaag-color-blue: #1261a3; - /* Background colors */ - --nldesign-color-background: #ffffff; - --nldesign-color-background-rgb: 255, 255, 255; + /* Background colors - for hover states and dark variants. */ + /* Main background managed by Nextcloud theming system. */ --nldesign-color-background-hover: #f5f5f5; --nldesign-color-background-dark: #e8e8e8; --nldesign-color-background-darker: #d4d4d4; diff --git a/css/tokens/rijkshuisstijl.css b/css/tokens/rijkshuisstijl.css index e6ac083..eddbc6b 100644 --- a/css/tokens/rijkshuisstijl.css +++ b/css/tokens/rijkshuisstijl.css @@ -32,16 +32,16 @@ --rh-color-paars: #42145f; --rh-color-mauve: #b4a7c9; - /* Background colors */ - --nldesign-color-background: #ffffff; - --nldesign-color-background-rgb: 255, 255, 255; - --nldesign-color-background-hover: #f3f3f3; - --nldesign-color-background-dark: #e6e6e6; - --nldesign-color-background-darker: #cccccc; + /* Background colors - for hover states and dark variants. */ + /* Main background managed by Nextcloud theming system. */ + --nldesign-color-background-hover: #e8e9ea; + --nldesign-color-background-dark: #e0e1e2; + --nldesign-color-background-darker: #d0d1d2; /* Header */ --nldesign-color-header-background: #154273; - --nldesign-color-header-text: #ffffff; + --nldesign-color-header-text: #333333; + --nldesign-header-border-bottom: 0; /* Navigation */ --nldesign-color-nav-background: #ffffff; diff --git a/css/tokens/rotterdam.css b/css/tokens/rotterdam.css index 0a515eb..d60bea8 100644 --- a/css/tokens/rotterdam.css +++ b/css/tokens/rotterdam.css @@ -21,9 +21,8 @@ --rotterdam-color-orange: #ec6d00; --rotterdam-color-yellow: #ffc800; - /* Background colors */ - --nldesign-color-background: #ffffff; - --nldesign-color-background-rgb: 255, 255, 255; + /* Background colors - for hover states and dark variants. */ + /* Main background managed by Nextcloud theming system. */ --nldesign-color-background-hover: #f5f5f5; --nldesign-color-background-dark: #e8e8e8; --nldesign-color-background-darker: #d4d4d4; diff --git a/css/tokens/utrecht.css b/css/tokens/utrecht.css index 3bc6753..4e9afc0 100644 --- a/css/tokens/utrecht.css +++ b/css/tokens/utrecht.css @@ -23,9 +23,8 @@ --utrecht-color-green: #2a5510; --utrecht-color-blue: #007bc7; - /* Background colors */ - --nldesign-color-background: #ffffff; - --nldesign-color-background-rgb: 255, 255, 255; + /* Background colors - for hover states and dark variants. */ + /* Main background managed by Nextcloud theming system. */ --nldesign-color-background-hover: #f5f5f5; --nldesign-color-background-dark: #e8e8e8; --nldesign-color-background-darker: #d4d4d4; diff --git a/img/nederland-logo.svg b/img/nederland-logo.svg new file mode 100644 index 0000000..8b5e8d7 --- /dev/null +++ b/img/nederland-logo.svg @@ -0,0 +1,396 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/admin.js b/js/admin.js index 5c84c4c..773b566 100644 --- a/js/admin.js +++ b/js/admin.js @@ -4,6 +4,7 @@ document.addEventListener('DOMContentLoaded', function() { const tokenSetInputs = document.querySelectorAll('input[name="nldesign-token-set"]'); + const hideSloganCheckbox = document.getElementById('nldesign-hide-slogan'); const previewBox = document.querySelector('.nldesign-preview-box'); // Token set color mappings for preview @@ -104,4 +105,73 @@ document.addEventListener('DOMContentLoaded', function() { OC.Notification.showTemporary(t('nldesign', 'Failed to update theme.')); }); } + + // Handle hide slogan checkbox + if (hideSloganCheckbox) { + hideSloganCheckbox.addEventListener('change', function() { + const hideSlogan = this.checked; + saveSloganSetting(hideSlogan); + }); + } + + // Handle show menu labels checkbox + const showMenuLabelsCheckbox = document.getElementById('nldesign-show-menu-labels'); + if (showMenuLabelsCheckbox) { + showMenuLabelsCheckbox.addEventListener('change', function() { + const showMenuLabels = this.checked; + saveMenuLabelsSetting(showMenuLabels); + }); + } + + // Save hide slogan setting to server + function saveSloganSetting(hideSlogan) { + const url = OC.generateUrl('/apps/nldesign/settings/slogan'); + + fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'requesttoken': OC.requestToken + }, + body: JSON.stringify({ hideSlogan: hideSlogan }) + }) + .then(response => response.json()) + .then(data => { + if (data.status === 'ok') { + OC.Notification.showTemporary(t('nldesign', 'Setting saved successfully. Reload the login page to see changes.')); + } else { + OC.Notification.showTemporary(t('nldesign', 'Failed to save setting.')); + } + }) + .catch(error => { + console.error('Error saving slogan setting:', error); + OC.Notification.showTemporary(t('nldesign', 'Failed to save setting.')); + }); + } + + // Save show menu labels setting to server. + function saveMenuLabelsSetting(showMenuLabels) { + const url = OC.generateUrl('/apps/nldesign/settings/menulabels'); + + fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'requesttoken': OC.requestToken + }, + body: JSON.stringify({ showMenuLabels: showMenuLabels }) + }) + .then(response => response.json()) + .then(data => { + if (data.status === 'ok') { + OC.Notification.showTemporary(t('nldesign', 'Setting saved successfully. Reload the page to see changes.')); + } else { + OC.Notification.showTemporary(t('nldesign', 'Failed to save setting.')); + } + }) + .catch(error => { + console.error('Error saving menu labels setting:', error); + OC.Notification.showTemporary(t('nldesign', 'Failed to save setting.')); + }); + } }); diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index f9dce4d..787bc9e 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -1,5 +1,15 @@ + * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0-or-later + * @link https://github.com/ConductionNL/nldesign + */ + declare(strict_types=1); namespace OCA\NLDesign\AppInfo; @@ -10,42 +20,85 @@ use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; -class Application extends App implements IBootstrap { - public const APP_ID = 'nldesign'; - - public function __construct() { - parent::__construct(self::APP_ID); - } - - public function register(IRegistrationContext $context): void { - // Register the theme - } - - public function boot(IBootContext $context): void { - $serverContainer = $context->getServerContainer(); - - // Inject our CSS variables - $this->injectThemeCSS($serverContainer); - } - - private function injectThemeCSS($serverContainer): void { - $config = $serverContainer->getConfig(); - $tokenSet = $config->getAppValue(self::APP_ID, 'token_set', 'rijkshuisstijl'); - - // Add fonts (Fira Sans from @fontsource) - \OCP\Util::addStyle(self::APP_ID, 'fonts'); - - // Add the CSS file for the selected token set - \OCP\Util::addStyle(self::APP_ID, 'tokens/' . $tokenSet); - \OCP\Util::addStyle(self::APP_ID, 'theme'); - - // Add aggressive overrides last (highest priority) - \OCP\Util::addStyle(self::APP_ID, 'overrides'); - - // Add logo for the selected token set - \OCP\Util::addStyle(self::APP_ID, 'logo-' . $tokenSet); - - // Nuclear option for gradients (absolute last) - \OCP\Util::addStyle(self::APP_ID, 'nuclear'); - } +/** + * Main application class for NL Design. + * + * Bootstraps the NL Design theme system and injects design tokens. + */ +class Application extends App implements IBootstrap +{ + public const APP_ID = 'nldesign'; + + /** + * Constructor. + */ + public function __construct() + { + parent::__construct(self::APP_ID); + } + + /** + * Register services and providers. + * + * @param IRegistrationContext $context The registration context. + * + * @return void + */ + public function register(IRegistrationContext $context): void + { + // Register the theme. + } + + /** + * Boot the application. + * + * @param IBootContext $context The boot context. + * + * @return void + */ + public function boot(IBootContext $context): void + { + $serverContainer = $context->getServerContainer(); + + // Inject our CSS variables. + $this->injectThemeCSS($serverContainer); + } + + /** + * Inject theme CSS files based on configuration. + * + * @param mixed $serverContainer The server container. + * + * @return void + */ + private function injectThemeCSS($serverContainer): void + { + $config = $serverContainer->getConfig(); + $tokenSet = $config->getAppValue(self::APP_ID, 'token_set', 'rijkshuisstijl'); + $hideSlogan = $config->getAppValue(self::APP_ID, 'hide_slogan', '0') === '1'; + $showMenuLabels = $config->getAppValue(self::APP_ID, 'show_menu_labels', '0') === '1'; + + // Add fonts (Fira Sans from @fontsource). + \OCP\Util::addStyle(self::APP_ID, 'fonts'); + + // Add the CSS file for the selected token set (organization-specific tokens). + \OCP\Util::addStyle(self::APP_ID, 'tokens/' . $tokenSet); + + // Add theme CSS (standard design token application). + \OCP\Util::addStyle(self::APP_ID, 'theme'); + + // Add aggressive overrides (applies NL Design styling to Nextcloud). + // This includes header styling for logged-in pages. + \OCP\Util::addStyle(self::APP_ID, 'overrides'); + + // Hide slogan if enabled. + if ($hideSlogan) { + \OCP\Util::addStyle(self::APP_ID, 'hide-slogan'); + } + + // Show menu labels (instead of icons) if enabled. + if ($showMenuLabels) { + \OCP\Util::addStyle(self::APP_ID, 'show-menu-labels'); + } + } } diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 651b69a..2e3eada 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -1,5 +1,15 @@ + * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0-or-later + * @link https://github.com/ConductionNL/nldesign + */ + declare(strict_types=1); namespace OCA\NLDesign\Controller; @@ -10,43 +20,108 @@ use OCP\IConfig; use OCP\IRequest; -class SettingsController extends Controller { - private IConfig $config; - - public function __construct( - string $appName, - IRequest $request, - IConfig $config - ) { - parent::__construct($appName, $request); - $this->config = $config; - } - - /** - * @AuthorizedAdminSetting(settings=OCA\NLDesign\Settings\Admin) - */ - public function setTokenSet(string $tokenSet): JSONResponse { - $validSets = ['rijkshuisstijl', 'utrecht', 'amsterdam', 'denhaag', 'rotterdam']; - - if (!in_array($tokenSet, $validSets)) { - return new JSONResponse(['error' => 'Invalid token set'], 400); - } - - $this->config->setAppValue(Application::APP_ID, 'token_set', $tokenSet); - - return new JSONResponse(['status' => 'ok', 'tokenSet' => $tokenSet]); - } - - /** - * @AuthorizedAdminSetting(settings=OCA\NLDesign\Settings\Admin) - */ - public function getTokenSet(): JSONResponse { - $tokenSet = $this->config->getAppValue( - Application::APP_ID, - 'token_set', - 'rijkshuisstijl' - ); - - return new JSONResponse(['tokenSet' => $tokenSet]); - } +/** + * Settings controller for NL Design app. + * + * Handles API requests for managing NL Design theme settings. + */ +class SettingsController extends Controller +{ + private IConfig $config; + + /** + * Constructor. + * + * @param string $appName The app name. + * @param IRequest $request The request object. + * @param IConfig $config The config service. + */ + public function __construct( + string $appName, + IRequest $request, + IConfig $config + ) { + parent::__construct($appName, $request); + $this->config = $config; + } + + /** + * Set the active design token set. + * + * @param string $tokenSet The token set name. + * + * @return JSONResponse The response with status and selected token set. + * + * @AuthorizedAdminSetting(settings=OCA\NLDesign\Settings\Admin) + */ + public function setTokenSet(string $tokenSet): JSONResponse + { + $validSets = ['rijkshuisstijl', 'utrecht', 'amsterdam', 'denhaag', 'rotterdam']; + + if (!in_array($tokenSet, $validSets)) { + return new JSONResponse(['error' => 'Invalid token set'], 400); + } + + $this->config->setAppValue(Application::APP_ID, 'token_set', $tokenSet); + + return new JSONResponse(['status' => 'ok', 'tokenSet' => $tokenSet]); + } + + /** + * Get the currently active design token set. + * + * @AuthorizedAdminSetting(settings=OCA\NLDesign\Settings\Admin) + * + * @return JSONResponse The response with the current token set. + */ + public function getTokenSet(): JSONResponse + { + $tokenSet = $this->config->getAppValue( + Application::APP_ID, + 'token_set', + 'rijkshuisstijl' + ); + + return new JSONResponse(['tokenSet' => $tokenSet]); + } + + /** + * Set the hide slogan setting. + * + * @param bool $hideSlogan Whether to hide the slogan on login page. + * + * @return JSONResponse The response with the status. + * + * @AuthorizedAdminSetting(settings=OCA\NLDesign\Settings\Admin) + */ + public function setSloganSetting(bool $hideSlogan): JSONResponse + { + $this->config->setAppValue( + Application::APP_ID, + 'hide_slogan', + $hideSlogan ? '1' : '0' + ); + + return new JSONResponse(['status' => 'ok', 'hideSlogan' => $hideSlogan]); + } + + /** + * Set the show menu labels setting. + * + * @param bool $showMenuLabels Whether to show text labels in app menu. + * + * @return JSONResponse The response with the status. + * + * @AuthorizedAdminSetting(settings=OCA\NLDesign\Settings\Admin) + */ + public function setMenuLabelsSetting(bool $showMenuLabels): JSONResponse + { + $this->config->setAppValue( + Application::APP_ID, + 'show_menu_labels', + $showMenuLabels ? '1' : '0' + ); + + return new JSONResponse(['status' => 'ok', 'showMenuLabels' => $showMenuLabels]); + } } diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index ca6133d..661f948 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -1,5 +1,15 @@ + * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0-or-later + * @link https://github.com/ConductionNL/nldesign + */ + declare(strict_types=1); namespace OCA\NLDesign\Settings; @@ -10,56 +20,103 @@ use OCP\IL10N; use OCP\Settings\ISettings; -class Admin implements ISettings { - private IConfig $config; - private IL10N $l; +/** + * Admin settings form for NL Design. + * + * Provides the configuration interface for selecting design token sets. + */ +class Admin implements ISettings +{ + private IConfig $config; + private IL10N $l; + + /** + * Constructor. + * + * @param IConfig $config The config service. + * @param IL10N $l The localization service. + */ + public function __construct(IConfig $config, IL10N $l) + { + $this->config = $config; + $this->l = $l; + } + + /** + * Get the settings form. + * + * @return TemplateResponse The settings form template. + */ + public function getForm(): TemplateResponse + { + $tokenSets = [ + 'rijkshuisstijl' => [ + 'name' => 'Rijkshuisstijl', + 'description' => $this->l->t('Dutch national government (Rijksoverheid)'), + ], + 'utrecht' => [ + 'name' => 'Gemeente Utrecht', + 'description' => $this->l->t('Municipality of Utrecht'), + ], + 'amsterdam' => [ + 'name' => 'Gemeente Amsterdam', + 'description' => $this->l->t('Municipality of Amsterdam'), + ], + 'denhaag' => [ + 'name' => 'Gemeente Den Haag', + 'description' => $this->l->t('Municipality of The Hague'), + ], + 'rotterdam' => [ + 'name' => 'Gemeente Rotterdam', + 'description' => $this->l->t('Municipality of Rotterdam'), + ], + ]; - public function __construct(IConfig $config, IL10N $l) { - $this->config = $config; - $this->l = $l; - } + $currentTokenSet = $this->config->getAppValue( + Application::APP_ID, + 'token_set', + 'rijkshuisstijl' + ); - public function getForm(): TemplateResponse { - $tokenSets = [ - 'rijkshuisstijl' => [ - 'name' => 'Rijkshuisstijl', - 'description' => $this->l->t('Dutch national government (Rijksoverheid)'), - ], - 'utrecht' => [ - 'name' => 'Gemeente Utrecht', - 'description' => $this->l->t('Municipality of Utrecht'), - ], - 'amsterdam' => [ - 'name' => 'Gemeente Amsterdam', - 'description' => $this->l->t('Municipality of Amsterdam'), - ], - 'denhaag' => [ - 'name' => 'Gemeente Den Haag', - 'description' => $this->l->t('Municipality of The Hague'), - ], - 'rotterdam' => [ - 'name' => 'Gemeente Rotterdam', - 'description' => $this->l->t('Municipality of Rotterdam'), - ], - ]; + $hideSlogan = $this->config->getAppValue( + Application::APP_ID, + 'hide_slogan', + '0' + ) === '1'; - $currentTokenSet = $this->config->getAppValue( - Application::APP_ID, - 'token_set', - 'rijkshuisstijl' - ); + $showMenuLabels = $this->config->getAppValue( + Application::APP_ID, + 'show_menu_labels', + '0' + ) === '1'; - return new TemplateResponse(Application::APP_ID, 'settings/admin', [ - 'tokenSets' => $tokenSets, - 'currentTokenSet' => $currentTokenSet, - ]); - } + return new TemplateResponse( + Application::APP_ID, 'settings/admin', [ + 'tokenSets' => $tokenSets, + 'currentTokenSet' => $currentTokenSet, + 'hideSlogan' => $hideSlogan, + 'showMenuLabels' => $showMenuLabels, + ] + ); + } - public function getSection(): string { - return 'theming'; - } + /** + * Get the settings section identifier. + * + * @return string The section identifier (theming). + */ + public function getSection(): string + { + return 'theming'; + } - public function getPriority(): int { - return 50; - } + /** + * Get the priority for ordering in the settings menu. + * + * @return int The priority value (lower = higher priority). + */ + public function getPriority(): int + { + return 50; + } } diff --git a/package.json b/package.json index 7ef32c1..eeaafc7 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "build": "npm run build:fonts && npm run build:icons", "build:fonts": "node scripts/build-fonts.js", "build:icons": "node scripts/build-icons.js", + "lint": "echo 'No JavaScript to lint - nldesign is CSS/PHP only'", "test": "echo 'No tests yet'" }, "dependencies": { diff --git a/scripts/build-fonts.js b/scripts/build-fonts.js old mode 100755 new mode 100644 index e1b7519..9727ff7 --- a/scripts/build-fonts.js +++ b/scripts/build-fonts.js @@ -12,14 +12,43 @@ const path = require('path'); const FONTS_DIR = path.join(__dirname, '..', 'css', 'fonts'); const FONTS_CSS = path.join(__dirname, '..', 'css', 'fonts.css'); +const FONTSOURCE_DIR = path.join(__dirname, '..', 'node_modules', '@fontsource', 'fira-sans', 'files'); -// Create fonts directory if it doesn't exist +// Create fonts directory if it doesn't exist. if (!fs.existsSync(FONTS_DIR)) { fs.mkdirSync(FONTS_DIR, { recursive: true }); console.log('βœ“ Created css/fonts directory'); } -// Generate fonts.css +// Copy font files from node_modules. +const fontFiles = [ + 'fira-sans-latin-400-normal.woff2', + 'fira-sans-latin-400-normal.woff', + 'fira-sans-latin-400-italic.woff2', + 'fira-sans-latin-400-italic.woff', + 'fira-sans-latin-700-normal.woff2', + 'fira-sans-latin-700-normal.woff', + 'fira-sans-latin-700-italic.woff2', + 'fira-sans-latin-700-italic.woff', +]; + +let copiedCount = 0; +if (fs.existsSync(FONTSOURCE_DIR)) { + fontFiles.forEach(file => { + const sourcePath = path.join(FONTSOURCE_DIR, file); + const destPath = path.join(FONTS_DIR, file); + if (fs.existsSync(sourcePath)) { + fs.copyFileSync(sourcePath, destPath); + copiedCount++; + } + }); + console.log(`βœ“ Copied ${copiedCount} font files to css/fonts/`); +} else { + console.log('⚠ Warning: @fontsource/fira-sans not found in node_modules'); + console.log(' Run: npm install'); +} + +// Generate fonts.css with correct paths. const fontsCss = `/** * Fira Sans Fonts * @@ -27,18 +56,11 @@ const fontsCss = `/** * from @rijkshuisstijl-community/font package */ -/* Import Fira Sans from fontsource */ -@import url('~@fontsource/fira-sans/400.css'); -@import url('~@fontsource/fira-sans/400-italic.css'); -@import url('~@fontsource/fira-sans/700.css'); -@import url('~@fontsource/fira-sans/700-italic.css'); - -/* Fallback for direct file access */ @font-face { font-family: 'Fira Sans'; src: local('Fira Sans'), - url('../fonts/fira-sans-400-normal.woff2') format('woff2'), - url('../fonts/fira-sans-400-normal.woff') format('woff'); + url('fonts/fira-sans-latin-400-normal.woff2') format('woff2'), + url('fonts/fira-sans-latin-400-normal.woff') format('woff'); font-weight: 400; font-style: normal; font-display: swap; @@ -47,8 +69,8 @@ const fontsCss = `/** @font-face { font-family: 'Fira Sans'; src: local('Fira Sans Italic'), - url('../fonts/fira-sans-400-italic.woff2') format('woff2'), - url('../fonts/fira-sans-400-italic.woff') format('woff'); + url('fonts/fira-sans-latin-400-italic.woff2') format('woff2'), + url('fonts/fira-sans-latin-400-italic.woff') format('woff'); font-weight: 400; font-style: italic; font-display: swap; @@ -57,8 +79,8 @@ const fontsCss = `/** @font-face { font-family: 'Fira Sans'; src: local('Fira Sans Bold'), - url('../fonts/fira-sans-700-normal.woff2') format('woff2'), - url('../fonts/fira-sans-700-normal.woff') format('woff'); + url('fonts/fira-sans-latin-700-normal.woff2') format('woff2'), + url('fonts/fira-sans-latin-700-normal.woff') format('woff'); font-weight: 700; font-style: normal; font-display: swap; @@ -67,8 +89,8 @@ const fontsCss = `/** @font-face { font-family: 'Fira Sans'; src: local('Fira Sans Bold Italic'), - url('../fonts/fira-sans-700-italic.woff2') format('woff2'), - url('../fonts/fira-sans-700-italic.woff') format('woff'); + url('fonts/fira-sans-latin-700-italic.woff2') format('woff2'), + url('fonts/fira-sans-latin-700-italic.woff') format('woff'); font-weight: 700; font-style: italic; font-display: swap; @@ -79,5 +101,5 @@ fs.writeFileSync(FONTS_CSS, fontsCss); console.log('βœ“ Generated css/fonts.css'); console.log('\nβœ… Font build complete!'); -console.log('\nNote: Fonts are loaded via CDN from @fontsource/fira-sans'); -console.log('To use local files, copy font files from node_modules/@fontsource/fira-sans/files/ to css/fonts/'); +console.log(` ${copiedCount} font files copied to css/fonts/`); + diff --git a/scripts/build-tokens.js b/scripts/build-tokens.js old mode 100755 new mode 100644 diff --git a/templates/settings/admin.php b/templates/settings/admin.php index c2f499d..baa8cc6 100644 --- a/templates/settings/admin.php +++ b/templates/settings/admin.php @@ -32,6 +32,30 @@ + +
+ checked> + +
+ + +
+ checked> + +
+

t('Preview')); ?>