django-admin-grove 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,7 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class GroveConfig(AppConfig):
5
+ name = "django_admin_grove"
6
+ verbose_name = "Grove admin theme"
7
+ default_auto_field = "django.db.models.BigAutoField"
@@ -0,0 +1,42 @@
1
+ /* Print layout for the whole admin: A4 landscape, no chrome, content only. */
2
+ @page {
3
+ size: A4 landscape;
4
+ margin: 12mm;
5
+ }
6
+
7
+ /* Admin chrome and interactive controls */
8
+ #header, #nav-sidebar, #toggle-nav-sidebar, .breadcrumbs, #footer,
9
+ .messagelist, .object-tools,
10
+ .report-screen-only,
11
+ /* changelist controls */
12
+ #changelist-filter, #changelist-search, #changelist #toolbar, #changelist .actions,
13
+ .action-checkbox, .action-checkbox-column, #changelist .paginator a,
14
+ /* change form controls */
15
+ .submit-row, .related-widget-wrapper-link { display: none !important; }
16
+
17
+ html, body { background: #fff !important; color: #000 !important; }
18
+ #container, #main, #content, #content-main, #changelist, #changelist .results {
19
+ margin: 0 !important;
20
+ padding: 0 !important;
21
+ width: auto !important;
22
+ min-width: 0 !important;
23
+ max-width: none !important;
24
+ box-shadow: none !important;
25
+ border: 0 !important;
26
+ border-radius: 0 !important;
27
+ overflow: visible !important;
28
+ }
29
+ #content h1 { font-size: 16pt; margin: 0 0 4pt; }
30
+ .report-print-only { display: block !important; margin: 0 0 8pt; font-size: 10pt; color: #333; }
31
+
32
+ .module { border: 0 !important; border-radius: 0 !important; box-shadow: none !important; }
33
+ table { width: 100%; border-collapse: collapse; font-size: 9pt; }
34
+ thead { display: table-header-group; }
35
+ tr { page-break-inside: avoid; }
36
+ th, td { padding: 2pt 4pt !important; border-bottom: 1px solid #ccc; background: transparent !important; color: #000 !important; }
37
+ thead th { background: #eee !important; }
38
+ thead th .text a, thead th .sortoptions { color: #000 !important; }
39
+ a, a:link, a:visited { color: inherit !important; text-decoration: none !important; }
40
+
41
+ /* Detail blocks print as the reader left them (open or closed). */
42
+ details > summary { color: #000; }
@@ -0,0 +1,262 @@
1
+ /* Grove admin: forest greens and rounded surfaces.
2
+ Loaded after Django's page-specific and responsive styles. */
3
+ :root, html[data-theme="light"], html[data-theme="dark"] {
4
+ --theme-bg: #ffffff;
5
+ --theme-surface: #f4f8f6;
6
+ --theme-text: #13201a;
7
+ --theme-muted: #5b6b63;
8
+ --theme-border: #dde7e2;
9
+ --theme-brand: #0c4b33;
10
+ --theme-accent: #2f9e73;
11
+ --theme-soft: #e3f4ec;
12
+ --dca-primary: var(--theme-brand);
13
+ --dca-secondary: var(--theme-soft);
14
+ --theme-on-brand: #ffffff;
15
+ --theme-warning: #b45309;
16
+ --theme-warning-bg: #fdf1e3;
17
+ --theme-error: #a52a36;
18
+ --theme-error-bg: #fff0f1;
19
+ --theme-radius: 14px;
20
+ --primary: var(--theme-brand);
21
+ --secondary: var(--theme-brand);
22
+ --accent: var(--theme-accent);
23
+ --primary-fg: var(--theme-on-brand);
24
+ --body-fg: var(--theme-text);
25
+ --body-bg: var(--theme-bg);
26
+ --body-quiet-color: var(--theme-muted);
27
+ --body-medium-color: var(--theme-muted);
28
+ --body-loud-color: var(--theme-text);
29
+ --header-color: var(--theme-muted);
30
+ --header-branding-color: var(--theme-text);
31
+ --header-bg: var(--theme-bg);
32
+ --header-link-color: var(--theme-brand);
33
+ --breadcrumbs-fg: var(--theme-muted);
34
+ --breadcrumbs-link-fg: var(--theme-brand);
35
+ --breadcrumbs-bg: var(--theme-surface);
36
+ --link-fg: var(--theme-brand);
37
+ --link-hover-color: var(--theme-accent);
38
+ --link-selected-fg: var(--theme-brand);
39
+ --hairline-color: var(--theme-border);
40
+ --border-color: var(--theme-border);
41
+ --error-fg: var(--theme-error);
42
+ --message-success-bg: var(--theme-soft);
43
+ --message-warning-bg: var(--theme-warning-bg);
44
+ --message-error-bg: var(--theme-error-bg);
45
+ --darkened-bg: var(--theme-surface);
46
+ --selected-bg: var(--theme-soft);
47
+ --selected-row: var(--theme-soft);
48
+ --button-fg: var(--theme-on-brand);
49
+ --button-bg: var(--theme-brand);
50
+ --button-hover-bg: #166445;
51
+ --default-button-bg: var(--theme-brand);
52
+ --default-button-hover-bg: #166445;
53
+ --close-button-bg: var(--theme-muted);
54
+ --close-button-hover-bg: var(--theme-text);
55
+ --delete-button-bg: var(--theme-error);
56
+ --delete-button-hover-bg: #82202a;
57
+ --object-tools-fg: var(--theme-on-brand);
58
+ --object-tools-bg: var(--theme-brand);
59
+ --object-tools-hover-bg: #166445;
60
+ --object-tools-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4l4 4 4-4' fill='none' stroke='%23ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
61
+ --font-family-primary: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
62
+ --font-family-monospace: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
63
+ }
64
+ @media (prefers-color-scheme: dark) {
65
+ :root {
66
+ --theme-bg: #0d1512;
67
+ --theme-surface: #121d18;
68
+ --theme-text: #e7efeb;
69
+ --theme-muted: #9aaba3;
70
+ --theme-border: #22322b;
71
+ --theme-brand: #7fd8b1;
72
+ --theme-accent: #44b78b;
73
+ --theme-soft: #163327;
74
+ --theme-on-brand: #0d1512;
75
+ --theme-warning: #f5a54a;
76
+ --theme-warning-bg: #33230f;
77
+ --theme-error: #ff9da6;
78
+ --theme-error-bg: #351c22;
79
+ --button-hover-bg: #a1e5c6;
80
+ --default-button-hover-bg: #a1e5c6;
81
+ --object-tools-hover-bg: #a1e5c6;
82
+ --object-tools-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4l4 4 4-4' fill='none' stroke='%230d1512' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
83
+ --delete-button-hover-bg: #ffc2c8;
84
+ }
85
+ }
86
+ html[data-theme="dark"] {
87
+ --theme-bg: #0d1512;
88
+ --theme-surface: #121d18;
89
+ --theme-text: #e7efeb;
90
+ --theme-muted: #9aaba3;
91
+ --theme-border: #22322b;
92
+ --theme-brand: #7fd8b1;
93
+ --theme-accent: #44b78b;
94
+ --theme-soft: #163327;
95
+ --theme-on-brand: #0d1512;
96
+ --theme-warning: #f5a54a;
97
+ --theme-warning-bg: #33230f;
98
+ --theme-error: #ff9da6;
99
+ --theme-error-bg: #351c22;
100
+ --button-hover-bg: #a1e5c6;
101
+ --default-button-hover-bg: #a1e5c6;
102
+ --object-tools-hover-bg: #a1e5c6;
103
+ --object-tools-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4l4 4 4-4' fill='none' stroke='%230d1512' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
104
+ --delete-button-hover-bg: #ffc2c8;
105
+ }
106
+ body { font-family: var(--font-family-primary); -webkit-font-smoothing: antialiased; }
107
+ h1, h2, h3, h4 { letter-spacing: -.015em; }
108
+ #content h1 { color: var(--theme-text); font-weight: 700; font-size: 1.7rem; }
109
+ #header {
110
+ background: var(--theme-bg);
111
+ border-bottom: 1px solid var(--theme-border);
112
+ padding: 20px 32px;
113
+ gap: 16px;
114
+ flex-wrap: wrap;
115
+ }
116
+ #site-name { margin: 0; font-size: 1.2rem; font-weight: 700; }
117
+ #site-name a { display: inline-flex; align-items: center; gap: 10px; color: var(--theme-text); }
118
+
119
+ #user-tools { color: var(--theme-muted); line-height: 1.7; }
120
+ #header a:link, #header a:visited { color: var(--theme-brand); }
121
+ #user-tools a, #logout-form button { border: 0; color: var(--theme-brand); }
122
+ div.breadcrumbs { padding: 14px 32px; border-bottom: 1px solid var(--theme-border); }
123
+ #content { padding: 28px 32px; }
124
+ nav .module, #content-main>.module {
125
+ overflow: hidden;
126
+ border: 1px solid var(--theme-border);
127
+ border-radius: var(--theme-radius);
128
+ background: var(--theme-bg);
129
+ box-shadow: 0 12px 30px -26px rgba(12, 75, 51, .45);
130
+ }
131
+ .module h2, .module caption, .inline-group h2 {
132
+ background: var(--theme-surface);
133
+ color: var(--theme-text);
134
+ padding: 12px 16px;
135
+ font-weight: 600;
136
+ }
137
+ .module > h2, .module > table > caption, .inline-group > h2 {
138
+ border-radius: 13px 13px 0 0;
139
+ border-bottom: 1px solid var(--theme-border);
140
+ }
141
+ .module caption a:link, .module caption a:visited { color: var(--theme-brand); }
142
+ .module table { width: 100%; }
143
+ th, td { padding: 12px 14px; border-color: var(--theme-border); }
144
+ thead th {
145
+ background: var(--theme-surface);
146
+ color: var(--theme-muted);
147
+ font-size: .75rem;
148
+ font-weight: 600;
149
+ }
150
+ thead th.sorted { background: var(--theme-soft); }
151
+ .row1, .row2 { background: var(--theme-bg); }
152
+ #result_list tbody tr:hover { background: var(--theme-surface); }
153
+ #changelist { border-radius: var(--theme-radius); }
154
+ #changelist .results { overflow-x: auto; }
155
+ #changelist #toolbar { padding: 14px 16px; background: var(--theme-surface); border-color: var(--theme-border); }
156
+ #changelist .actions { background: var(--theme-bg); padding: 12px 16px; }
157
+ #changelist .paginator { background: var(--theme-surface); padding: 12px 16px; border-radius: 0 0 13px 13px; }
158
+ #changelist-filter { background: var(--theme-surface); border-inline-start: 1px solid var(--theme-border); }
159
+ #changelist-filter h2 { background: var(--theme-surface); color: var(--theme-text); }
160
+ #changelist-filter h3 { font-size: .9rem; color: var(--theme-text); }
161
+ #nav-sidebar { background: var(--theme-surface); border-color: var(--theme-border); padding: 12px 8px; }
162
+ #nav-sidebar .module { margin-bottom: 12px; }
163
+ #nav-sidebar th, #nav-sidebar td { padding: 10px; }
164
+ #nav-sidebar .current-model { background: var(--theme-soft); }
165
+ #toggle-nav-sidebar { background: var(--theme-bg); border-color: var(--theme-border); color: var(--theme-brand); }
166
+ input, textarea, select, button { font-family: inherit; }
167
+ input[type="text"], input[type="password"], input[type="email"], input[type="url"],
168
+ input[type="number"], input[type="date"], input[type="search"], input[type="tel"],
169
+ textarea, select, .vTextField, .vLargeTextField {
170
+ background: var(--theme-bg);
171
+ color: var(--theme-text);
172
+ border: 1px solid var(--theme-border);
173
+ border-radius: 8px;
174
+ padding: 9px 11px;
175
+ }
176
+ select { height: auto; min-height: 38px; line-height: 1.4; }
177
+ input[type="checkbox"], input[type="radio"] { accent-color: var(--theme-accent); }
178
+ a:focus-visible, button:focus-visible, input:focus-visible,
179
+ textarea:focus-visible, select:focus-visible {
180
+ outline: 2px solid var(--theme-accent);
181
+ outline-offset: 3px;
182
+ }
183
+ .button, input[type="submit"], input[type="button"], .submit-row input,
184
+ button[type="submit"] {
185
+ border: 1px solid transparent;
186
+ border-radius: 999px;
187
+ background: var(--theme-brand);
188
+ color: var(--theme-on-brand);
189
+ padding: 10px 18px;
190
+ font-weight: 600;
191
+ cursor: pointer;
192
+ }
193
+ .button:hover, input[type="submit"]:hover, input[type="button"]:hover,
194
+ button[type="submit"]:hover:hover { background: var(--button-hover-bg); }
195
+ #logout-form button { padding: 0; border: 0; border-radius: 0; background: transparent; color: var(--theme-brand); font-weight: inherit; }
196
+ /* Tool links and the actions select share one box. The selectors match
197
+ Django's own .object-tools a:link specificity so the padding applies. */
198
+ .object-tools a:link, .object-tools a:visited, .object-tools select {
199
+ display: block;
200
+ float: left;
201
+ box-sizing: border-box;
202
+ margin: 0;
203
+ padding: 8px 16px;
204
+ border: 1px solid transparent;
205
+ border-radius: 999px;
206
+ background-color: var(--object-tools-bg);
207
+ color: var(--object-tools-fg);
208
+ font-size: 0.6875rem;
209
+ font-weight: 600;
210
+ line-height: 1.25rem;
211
+ letter-spacing: 0.5px;
212
+ text-transform: uppercase;
213
+ }
214
+ .object-tools a:hover, .object-tools a:focus, .object-tools select:hover { background-color: var(--object-tools-hover-bg); }
215
+ .object-tools a.addlink, .object-tools a.viewsitelink { padding-right: 32px; background-position: right 12px center; }
216
+ .object-tools li { height: auto; margin-left: 10px; }
217
+ .object-tools select {
218
+ min-height: 0;
219
+ padding-right: 38px;
220
+ background-image: var(--object-tools-chevron);
221
+ background-repeat: no-repeat;
222
+ background-position: right 14px center;
223
+ appearance: none;
224
+ -webkit-appearance: none;
225
+ cursor: pointer;
226
+ }
227
+ .object-tools select:focus-visible { outline-offset: 2px; }
228
+ .object-tools select option { background: var(--theme-bg); color: var(--theme-text); font-weight: 400; text-transform: none; letter-spacing: 0; }
229
+ .submit-row { background: var(--theme-surface); border: 1px solid var(--theme-border); border-radius: var(--theme-radius); padding: 16px; }
230
+ .submit-row a.deletelink { border-radius: 999px; }
231
+ .form-row { padding: 16px; border-color: var(--theme-border); }
232
+ .form-row label { color: var(--theme-text); }
233
+ .help, p.help, .quiet { color: var(--theme-muted); }
234
+ .inline-group { border: 1px solid var(--theme-border); border-radius: var(--theme-radius); }
235
+ .inline-group .module { border: 0; border-radius: 0; box-shadow: none; }
236
+ .inline-group .tabular { overflow-x: auto; }
237
+ /* The row label is absolutely positioned inside td.original and sits at that
238
+ cell's static top; it must keep Django's 2px padding while the other cells
239
+ make room below it. */
240
+ .inline-group .tabular tr.has_original td { padding-top: 2.25rem; }
241
+ .inline-group .tabular tr.has_original td.original { padding-top: 2px; }
242
+ .inline-group .tabular tr.add-row td { background: var(--theme-soft); padding: 12px 16px; }
243
+ ul.messagelist li { border-radius: 10px; margin: 12px 0; color: var(--theme-text); }
244
+ .errornote, ul.errorlist { color: var(--theme-error); background: var(--theme-error-bg); border-color: var(--theme-error); }
245
+ .login #container { border: 1px solid var(--theme-border); border-radius: var(--theme-radius); box-shadow: 0 20px 50px -30px rgba(12, 75, 51, .45); }
246
+ .login #header { border-radius: 13px 13px 0 0; padding: 20px 24px; }
247
+ .login #content { padding: 24px; }
248
+ .login .submit-row { background: transparent; border: 0; }
249
+ .login .form-row input { box-sizing: border-box; }
250
+ @media (max-width: 767px) {
251
+ #header { padding: 16px; }
252
+ #content { padding: 20px 16px; }
253
+ div.breadcrumbs { padding: 12px 16px; }
254
+ #content h1 { font-size: 1.4rem; }
255
+ .form-row { padding: 12px; }
256
+ .submit-row { gap: 8px; }
257
+ .login #container { width: auto; max-width: 28rem; margin: 40px 16px; }
258
+ }
259
+ @media (prefers-reduced-motion: reduce) {
260
+ *, *::before, *::after { scroll-behavior: auto; }
261
+ }
262
+ .report-print-only { display: none; }
@@ -0,0 +1,9 @@
1
+ {% extends "admin/base.html" %}
2
+ {% load static %}
3
+
4
+ {% block responsive %}
5
+ {{ block.super }}
6
+ <link rel="stylesheet" href="{% static 'django_admin_grove/css/theme.css' %}">
7
+ <link rel="stylesheet" media="print" href="{% static 'django_admin_grove/css/print.css' %}">
8
+ {% block grove_styles %}{% endblock %}
9
+ {% endblock %}
@@ -0,0 +1,91 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-admin-grove
3
+ Version: 0.1.0
4
+ Summary: A forest-green theme for Django admin, with light, dark, and print styles.
5
+ Author: Fabian Braun
6
+ Classifier: Framework :: Django
7
+ Classifier: Framework :: Django :: 5.2
8
+ Classifier: Framework :: Django :: 6.0
9
+ Classifier: Programming Language :: Python :: 3
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: Django<6.1,>=5.2
13
+
14
+ # Django Admin Grove
15
+
16
+ A forest-green theme for Django admin with rounded surfaces, light and dark modes,
17
+ responsive layouts, and a print stylesheet. No JavaScript, external fonts, models,
18
+ or migrations are required. Uses Django's existing theme toggle and branding.
19
+
20
+ ## Installation
21
+
22
+ Install directly from Codeberg:
23
+
24
+ ```sh
25
+ python -m pip install git+https://codeberg.org/fsbraun/django-admin-grove
26
+ ```
27
+
28
+ Add Grove **before** Django's admin app (including custom AdminConfig subclasses):
29
+
30
+ ```python
31
+ INSTALLED_APPS = [
32
+ "django_admin_grove",
33
+ "django.contrib.admin",
34
+ # ...
35
+ ]
36
+ ```
37
+
38
+ Use Django's app template loader (`APP_DIRS = True`, or the explicit
39
+ `django.template.loaders.app_directories.Loader`) and staticfiles as usual.
40
+ Run `python manage.py collectstatic` for production deployments.
41
+ No URL configuration or database changes are needed. Django CMS is optional.
42
+
43
+ Supports Django 5.2 and 6.0; use a Python version supported by your Django release.
44
+
45
+ ## Customization
46
+
47
+ Set `admin.site.site_header`, `site_title`, and `index_title` normally, or override
48
+ `admin/base_site.html` in your project. Grove leaves Django's branding blocks intact.
49
+ Project templates should extend `admin/base.html` and keep `block.super` when
50
+ replacing the `responsive` block.
51
+
52
+ Load project CSS after Grove using its `grove_styles` block:
53
+
54
+ ```django
55
+ {% extends "admin/base.html" %}
56
+ {% load static %}
57
+ {% block grove_styles %}
58
+ <link rel="stylesheet" href="{% static 'myproject/admin.css' %}">
59
+ {% endblock %}
60
+ ```
61
+
62
+ Override the `--theme-*` CSS variables for colors and surfaces. Match Grove's
63
+ `html[data-theme="light"]` and `html[data-theme="dark"]` selectors when customizing
64
+ both modes. System preferences apply when Django selects automatic mode.
65
+ The `--dca-primary` and `--dca-secondary` variables follow the active palette.
66
+
67
+ Printing uses A4 landscape and hides admin controls. Use `.report-screen-only`
68
+ to hide custom controls in print and `.report-print-only` for print-only context.
69
+ Domain-specific selectors and branding belong in the consuming project.
70
+
71
+ ## Development
72
+
73
+ ```sh
74
+ python -m pip install -e .
75
+ python -m django test tests --settings=tests.settings
76
+ python -m pip install build
77
+ python -m build
78
+ ```
79
+
80
+ Tests run against plain Django admin with an in-memory database.
81
+
82
+ ## Publishing
83
+
84
+ Enable Actions in the Codeberg repository settings and make a runner with the
85
+ `codeberg-local` label available. Add a PyPI API token as the Actions secret
86
+ `PYPI_API_TOKEN`.
87
+
88
+ Update the version in `pyproject.toml`, then publish a Codeberg release with a
89
+ matching tag (for example, `0.1.0` or `v0.1.0`). The publication workflow checks
90
+ the version, runs the tests, builds and validates the wheel and source
91
+ distribution, and uploads both to PyPI.
@@ -0,0 +1,9 @@
1
+ django_admin_grove/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ django_admin_grove/apps.py,sha256=OK_8GWFFX07pwdiqYTc67T9nKAKnaN2k9s7Huy037gY,194
3
+ django_admin_grove/static/django_admin_grove/css/print.css,sha256=hhIJyD3jP0GyTS2kh5_YOq8ijbM6Plow-0cgOhE-wpg,1777
4
+ django_admin_grove/static/django_admin_grove/css/theme.css,sha256=4MicT4RCQUBYlyfRvgY1f0SbsQeuKfK191Ze4SVwd_o,11798
5
+ django_admin_grove/templates/admin/base.html,sha256=8g2Lw7JLA63W-44f2NyCJfqU8CVshNw0D-0PkKQFR1o,318
6
+ django_admin_grove-0.1.0.dist-info/METADATA,sha256=c3pylesa7-h4MOIUW_fnFBORARM0j-hELXeaOcaaV6U,3142
7
+ django_admin_grove-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
8
+ django_admin_grove-0.1.0.dist-info/top_level.txt,sha256=5PMtze9yfOVQVGUkubEd7H7KA2eTtQNbxMJgce0fJnQ,19
9
+ django_admin_grove-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ django_admin_grove