django-kpforms 0.1.1__tar.gz

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.
Files changed (75) hide show
  1. django_kpforms-0.1.1/.gitignore +148 -0
  2. django_kpforms-0.1.1/PKG-INFO +414 -0
  3. django_kpforms-0.1.1/kpforms/README.md +390 -0
  4. django_kpforms-0.1.1/kpforms/__init__.py +26 -0
  5. django_kpforms-0.1.1/kpforms/admin.py +3 -0
  6. django_kpforms-0.1.1/kpforms/apps.py +7 -0
  7. django_kpforms-0.1.1/kpforms/form_schema.py +236 -0
  8. django_kpforms-0.1.1/kpforms/helpers.py +377 -0
  9. django_kpforms-0.1.1/kpforms/migrations/0001_initial.py +60 -0
  10. django_kpforms-0.1.1/kpforms/migrations/__init__.py +0 -0
  11. django_kpforms-0.1.1/kpforms/models.py +75 -0
  12. django_kpforms-0.1.1/kpforms/static/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  13. django_kpforms-0.1.1/kpforms/static/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  14. django_kpforms-0.1.1/kpforms/static/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  15. django_kpforms-0.1.1/kpforms/static/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  16. django_kpforms-0.1.1/kpforms/static/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  17. django_kpforms-0.1.1/kpforms/static/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  18. django_kpforms-0.1.1/kpforms/static/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  19. django_kpforms-0.1.1/kpforms/static/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  20. django_kpforms-0.1.1/kpforms/static/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  21. django_kpforms-0.1.1/kpforms/static/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  22. django_kpforms-0.1.1/kpforms/static/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  23. django_kpforms-0.1.1/kpforms/static/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  24. django_kpforms-0.1.1/kpforms/static/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  25. django_kpforms-0.1.1/kpforms/static/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  26. django_kpforms-0.1.1/kpforms/static/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  27. django_kpforms-0.1.1/kpforms/static/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  28. django_kpforms-0.1.1/kpforms/static/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  29. django_kpforms-0.1.1/kpforms/static/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  30. django_kpforms-0.1.1/kpforms/static/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  31. django_kpforms-0.1.1/kpforms/static/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  32. django_kpforms-0.1.1/kpforms/static/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  33. django_kpforms-0.1.1/kpforms/static/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  34. django_kpforms-0.1.1/kpforms/static/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  35. django_kpforms-0.1.1/kpforms/static/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  36. django_kpforms-0.1.1/kpforms/static/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  37. django_kpforms-0.1.1/kpforms/static/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  38. django_kpforms-0.1.1/kpforms/static/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  39. django_kpforms-0.1.1/kpforms/static/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  40. django_kpforms-0.1.1/kpforms/static/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  41. django_kpforms-0.1.1/kpforms/static/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  42. django_kpforms-0.1.1/kpforms/static/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  43. django_kpforms-0.1.1/kpforms/static/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  44. django_kpforms-0.1.1/kpforms/static/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  45. django_kpforms-0.1.1/kpforms/static/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  46. django_kpforms-0.1.1/kpforms/static/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  47. django_kpforms-0.1.1/kpforms/static/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  48. django_kpforms-0.1.1/kpforms/static/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  49. django_kpforms-0.1.1/kpforms/static/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  50. django_kpforms-0.1.1/kpforms/static/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  51. django_kpforms-0.1.1/kpforms/static/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  52. django_kpforms-0.1.1/kpforms/static/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  53. django_kpforms-0.1.1/kpforms/static/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  54. django_kpforms-0.1.1/kpforms/static/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  55. django_kpforms-0.1.1/kpforms/static/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  56. django_kpforms-0.1.1/kpforms/static/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  57. django_kpforms-0.1.1/kpforms/static/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  58. django_kpforms-0.1.1/kpforms/static/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  59. django_kpforms-0.1.1/kpforms/static/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  60. django_kpforms-0.1.1/kpforms/static/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  61. django_kpforms-0.1.1/kpforms/static/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  62. django_kpforms-0.1.1/kpforms/static/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  63. django_kpforms-0.1.1/kpforms/static/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  64. django_kpforms-0.1.1/kpforms/static/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  65. django_kpforms-0.1.1/kpforms/static/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  66. django_kpforms-0.1.1/kpforms/static/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  67. django_kpforms-0.1.1/kpforms/static/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  68. django_kpforms-0.1.1/kpforms/static/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  69. django_kpforms-0.1.1/kpforms/static/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  70. django_kpforms-0.1.1/kpforms/static/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  71. django_kpforms-0.1.1/kpforms/static/html2pdf.bundle.min.js +3 -0
  72. django_kpforms-0.1.1/kpforms/static/html2pdf.bundle.min.js.map +1 -0
  73. django_kpforms-0.1.1/kpforms/static/kpforms.js +5830 -0
  74. django_kpforms-0.1.1/kpforms/views.py +2 -0
  75. django_kpforms-0.1.1/pyproject.toml +55 -0
@@ -0,0 +1,148 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ *.py,cover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+ cover/
51
+
52
+ # Translations
53
+ *.mo
54
+ *.pot
55
+
56
+ # Django stuff:
57
+ *.log
58
+ local_settings.py
59
+ db.sqlite3
60
+ db.sqlite3-journal
61
+ staticfiles/
62
+ media/
63
+
64
+ # Scrapy stuff:
65
+ .scrapy
66
+
67
+ # Sphinx documentation
68
+ docs/_build/
69
+
70
+ # PyBuilder
71
+ .pybuilder/
72
+ target/
73
+
74
+ # Jupyter Notebook
75
+ .ipynb_checkpoints
76
+
77
+ # IPython
78
+ profile_default/
79
+ ipython_config.py
80
+
81
+ # pyenv
82
+ # .python-version
83
+
84
+ # pdm
85
+ __pypackages__/
86
+
87
+ # Celery stuff
88
+ celerybeat-schedule
89
+ celerybeat.pid
90
+
91
+ # SageMath parsed files
92
+ *.sage.py
93
+
94
+ # Environments
95
+ .env
96
+ .venv
97
+ env/
98
+ venv/
99
+ ENV/
100
+ env.bak/
101
+ venv.bak/
102
+
103
+ # Spyder project settings
104
+ .spyderproject
105
+ .spyproject
106
+
107
+ # Rope project settings
108
+ .ropeproject
109
+
110
+ # mkdocs documentation
111
+ /site
112
+
113
+ # mypy
114
+ .mypy_cache/
115
+ .dmypy.json
116
+ dmypy.json
117
+
118
+ # Pyre type checker
119
+ .pyre/
120
+
121
+ # pytype static type analyzer
122
+ .pytype/
123
+
124
+ # Cython debug symbols
125
+ cython_debug/
126
+
127
+ # OS
128
+ .DS_Store
129
+ Thumbs.db
130
+
131
+ # IDEs
132
+ .idea/
133
+ .vscode/
134
+ *.swp
135
+ *.swo
136
+
137
+ .junie/
138
+
139
+ # Node.js / TypeScript / Lit (kpforms/kpforms)
140
+ node_modules/
141
+ dist-ssr/
142
+ *.local
143
+ npm-debug.log*
144
+ yarn-debug.log*
145
+ yarn-error.log*
146
+ pnpm-debug.log*
147
+ .vite/
148
+ .eslintcache
@@ -0,0 +1,414 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-kpforms
3
+ Version: 0.1.1
4
+ Summary: Reusable Django app for dynamic form templates and submissions
5
+ Project-URL: Homepage, https://github.com/kodexprojects/django-kpforms
6
+ Project-URL: Bug Tracker, https://github.com/kodexprojects/django-kpforms/issues
7
+ License-Expression: MIT
8
+ Classifier: Environment :: Web Environment
9
+ Classifier: Framework :: Django
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Topic :: Internet :: WWW/HTTP
16
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
17
+ Requires-Python: >=3.14
18
+ Requires-Dist: django>=5.0
19
+ Requires-Dist: pydantic>=2.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest-django>=4.0; extra == 'dev'
22
+ Requires-Dist: pytest>=8.0; extra == 'dev'
23
+ Description-Content-Type: text/markdown
24
+
25
+ # django-kpforms
26
+
27
+ A reusable Django application for **dynamic form templates and submissions**, powered by Lit-based web components.
28
+
29
+ - Define form templates with sections, groups, and typed fields (numeric, string, text, radio, checkbox, datetime, calculated, ranged-radio)
30
+ - Built-in tolerance checking for numeric and string fields
31
+ - Interactive web component (`<dynamic-form>`) for end-user submission
32
+ - Visual form-builder web component (`<dynamic-form-builder>`) for creating and editing templates
33
+ - Printable-form component for generating PDFs via `html2pdf.js`
34
+ - Full version history for form templates
35
+
36
+ ---
37
+
38
+ ## Table of Contents
39
+
40
+ 1. [Requirements](#requirements)
41
+ 2. [Installation](#installation)
42
+ 3. [Quick Start](#quick-start)
43
+ 4. [Django Models & Helpers](#django-models--helpers)
44
+ 5. [Web Components](#web-components)
45
+ - [`<dynamic-form>`](#dynamic-form)
46
+ - [`<dynamic-form-builder>`](#dynamic-form-builder)
47
+ - [`<printable-form>`](#printable-form)
48
+ 6. [CSS Customisation Variables](#css-customisation-variables)
49
+ - [Typography](#typography)
50
+ - [Colours](#colours)
51
+ - [Accent Colour](#accent-colour)
52
+ - [Status / Tolerance Colours](#status--tolerance-colours)
53
+ - [Layout](#layout)
54
+ 7. [Contributing / Building from Source](#contributing--building-from-source)
55
+
56
+ ---
57
+
58
+ ## Requirements
59
+
60
+ - Python ≥ 3.14
61
+ - Django ≥ 5.0
62
+ - Pydantic ≥ 2.0
63
+
64
+ ---
65
+
66
+ ## Installation
67
+
68
+ ```bash
69
+ pip install django-kpforms
70
+ ```
71
+
72
+ Or with [uv](https://docs.astral.sh/uv/):
73
+
74
+ ```bash
75
+ uv add django-kpforms
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Quick Start
81
+
82
+ ### 1. Add to `INSTALLED_APPS`
83
+
84
+ ```python
85
+ # settings.py
86
+ INSTALLED_APPS = [
87
+ ...
88
+ "kpforms",
89
+ ]
90
+ ```
91
+
92
+ ### 2. Run migrations
93
+
94
+ ```bash
95
+ python manage.py migrate
96
+ ```
97
+
98
+ ### 3. Serve static files
99
+
100
+ `django-kpforms` ships with a compiled JavaScript bundle (`kpforms.js`) and
101
+ KaTeX font files under `kpforms/static/`.
102
+
103
+ Make sure Django's static-file serving is configured and `collectstatic` has
104
+ been run in production:
105
+
106
+ ```bash
107
+ python manage.py collectstatic
108
+ ```
109
+
110
+ ### 4. Load the bundle in your template
111
+
112
+ ```html
113
+ {% load static %}
114
+
115
+ <!-- Load the kpforms web-component bundle -->
116
+ <script type="module" src="{% static 'kpforms.js' %}"></script>
117
+
118
+ <!-- Optional: load html2pdf for the printable-form component -->
119
+ <script src="{% static 'html2pdf.bundle.min.js' %}"></script>
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Django Models & Helpers
125
+
126
+ ### Models
127
+
128
+ | Model | Description |
129
+ |---|---|
130
+ | `FormTemplate` | A versioned form definition. The `schema` field holds a `FormTemplateSchema` JSON object. |
131
+ | `FormTemplateVersionHistory` | Immutable snapshot of a `FormTemplate` at each version. |
132
+ | `FormSubmission` | A single submission against a `FormTemplate`. Validates and stores tolerance results. |
133
+
134
+ ### Helper functions (`kpforms.helpers`)
135
+
136
+ ```python
137
+ from kpforms.helpers import (
138
+ create_form_template,
139
+ update_form_template,
140
+ submit_form_data,
141
+ get_form_by_template_id_and_version,
142
+ get_form_template_list,
143
+ get_form_template_list_paginated,
144
+ get_all_results_for_form_template,
145
+ )
146
+ ```
147
+
148
+ | Function | Description |
149
+ |---|---|
150
+ | `create_form_template(schema, user)` | Creates a new `FormTemplate` from a `CreateFormTemplateSchema`. |
151
+ | `update_form_template(schema, user)` | Saves a new version of an existing template. |
152
+ | `submit_form_data(template_id, form_data, user)` | Validates and saves a `FormSubmission`. |
153
+ | `get_form_by_template_id_and_version(id, version=None)` | Returns the `FormTemplateSchema` for a given ID and optional version. |
154
+ | `get_form_template_list(only_active=True)` | Returns a list of `{id, name, description}` dicts. |
155
+ | `get_form_template_list_paginated(page_size, only_active)` | Returns a Django `Paginator`. |
156
+ | `get_all_results_for_form_template(template_id)` | Returns all submissions with labelled field data. |
157
+
158
+ ### Schema classes (`kpforms.form_schema`)
159
+
160
+ The core schema models are Pydantic models. The most important ones:
161
+
162
+ | Class | Description |
163
+ |---|---|
164
+ | `FormTemplateSchema` | Full form definition: `title`, `instruction`, `sections`. |
165
+ | `CreateFormTemplateSchema` | Input payload for creating a template. |
166
+ | `UpdateFormTemplateSchema` | Input payload for updating a template (adds `guid`). |
167
+ | `FormSubmissionSchema` | Submission payload: `data` (list of `{fieldId, value}`), `fieldsOutOfTolerance`. |
168
+
169
+ ---
170
+
171
+ ## Web Components
172
+
173
+ Include `kpforms.js` once on the page (see Quick Start above) — all components
174
+ are self-registering and require no additional setup.
175
+
176
+ ### `<dynamic-form>`
177
+
178
+ Renders a form for end-user completion and submission.
179
+
180
+ ```html
181
+ <dynamic-form id="my-form"></dynamic-form>
182
+
183
+ <script type="module">
184
+ const formEl = document.getElementById("my-form");
185
+
186
+ // Pass the form schema (FormSchema JSON) as a property
187
+ formEl.schema = {
188
+ guid: "...",
189
+ title: "My Form",
190
+ sections: [...],
191
+ generatePdfOption: false,
192
+ generatePdfString: "Generate PDF",
193
+ submitFormString: "Submit",
194
+ submittingString: "Submitting…",
195
+ apiConfig: {
196
+ url: "/api/forms/submit/",
197
+ method: "POST",
198
+ successMessage: "Form submitted!",
199
+ errorMessage: "Submission failed."
200
+ }
201
+ };
202
+ </script>
203
+ ```
204
+
205
+ **Properties / attributes:**
206
+
207
+ | Property | Type | Description |
208
+ |---|---|---|
209
+ | `schema` | `FormSchema` (object) | The full form schema to render. |
210
+ | `lang` | `string` | BCP-47 language tag for UI strings (e.g. `"en"`, `"sv"`). Defaults to `"en"`. |
211
+
212
+ ### `<dynamic-form-builder>`
213
+
214
+ An interactive editor that lets users create and edit form templates.
215
+
216
+ ```html
217
+ <dynamic-form-builder id="builder"></dynamic-form-builder>
218
+
219
+ <script type="module">
220
+ const builder = document.getElementById("builder");
221
+
222
+ // Optional: load an existing template for editing
223
+ builder.schema = { guid: "...", name: "...", formSchema: { ... } };
224
+
225
+ // Listen for save events
226
+ builder.addEventListener("form-save", (e) => {
227
+ const payload = e.detail; // CreateFormTemplateSchema | UpdateFormTemplateSchema
228
+ fetch("/api/forms/templates/", {
229
+ method: payload.guid ? "PUT" : "POST",
230
+ headers: { "Content-Type": "application/json" },
231
+ body: JSON.stringify(payload),
232
+ });
233
+ });
234
+ </script>
235
+ ```
236
+
237
+ **Properties / attributes:**
238
+
239
+ | Property | Type | Description |
240
+ |---|---|---|
241
+ | `schema` | `UpdateFormTemplateSchema` (object) | Optional existing template to edit. |
242
+ | `lang` | `string` | BCP-47 language tag. |
243
+
244
+ **Events emitted:**
245
+
246
+ | Event | `detail` | Description |
247
+ |---|---|---|
248
+ | `form-save` | `CreateFormTemplateSchema \| UpdateFormTemplateSchema` | Fired when the user clicks Save. |
249
+
250
+ ### `<printable-form>`
251
+
252
+ Renders a read-only printable view of a submitted form that can be exported as a PDF using `html2pdf.js`.
253
+
254
+ ```html
255
+ <printable-form id="print-form"></printable-form>
256
+
257
+ <script type="module">
258
+ const el = document.getElementById("print-form");
259
+ el.schema = { /* FormTemplateSchema */ };
260
+ el.submission = { /* FormSubmission data */ };
261
+ </script>
262
+ ```
263
+
264
+ **Properties:**
265
+
266
+ | Property | Type | Description |
267
+ |---|---|---|
268
+ | `schema` | `FormTemplateSchema` (object) | The form template. |
269
+ | `submission` | `FormSubmissionSchema` (object) | The submission data to display. |
270
+
271
+ ---
272
+
273
+ ## CSS Customisation Variables
274
+
275
+ All web components use CSS custom properties (variables) to control their appearance.
276
+ Set these on the element itself or any ancestor to theme the components.
277
+
278
+ The **public API** variables are prefixed `--df-*`. Internal variables map them
279
+ to `--form-*` and to [Shoelace](https://shoelace.style/) design tokens.
280
+
281
+ ### Typography
282
+
283
+ | Variable | Default | Description |
284
+ |---|---|---|
285
+ | `--df-font-size-base` | `1rem` | Base font size. |
286
+ | `--df-font-size-h1` | `1.8rem` | Form title size. |
287
+ | `--df-font-size-h2` | `1.4rem` | Section heading size. |
288
+ | `--df-font-size-label` | `0.9rem` | Field label size. |
289
+ | `--df-font-size-description` | `0.85rem` | Field description / helper text size. |
290
+
291
+ ### Colours
292
+
293
+ | Variable | Default | Description |
294
+ |---|---|---|
295
+ | `--df-color-text` | `#333` | Primary text colour. |
296
+ | `--df-color-input-text` | `#111` | Text colour inside input fields. |
297
+ | `--df-color-text-secondary` | `#555` | Secondary / description text colour. |
298
+ | `--df-color-text-label` | `#111` | Field label text colour. |
299
+ | `--df-color-background` | `#ffffff` | Component background. |
300
+ | `--df-color-background-input-base` | `#ffffff` | Input field background (resting). |
301
+ | `--df-color-background-input-focus` | `#ffffff` | Input field background on focus. |
302
+ | `--df-color-background-section` | `#f9f9f9` | Form section background. |
303
+ | `--df-color-border` | `#ccc` | Default border colour. |
304
+
305
+ ### Accent Colour
306
+
307
+ Used for buttons, active tabs, focused borders, and Shoelace's primary palette.
308
+
309
+ | Variable | Default | Description |
310
+ |---|---|---|
311
+ | `--df-color-accent` | `#007bff` | Primary accent / brand colour. |
312
+ | `--df-color-accent-lighter` | `#66b3ff` | Lighter variant (e.g. hover states). |
313
+ | `--df-color-accent-darker` | `#0056b3` | Darker variant (e.g. active states). |
314
+ | `--df-color-accent-text` | `#ffffff` | Text on accent-coloured elements. |
315
+ | `--df-color-success` | `#28a745` | Success colour (Shoelace success palette). |
316
+ | `--df-color-success-darker` | `#1e7e34` | Darker success variant. |
317
+ | `--df-color-success-text` | `#ffffff` | Text on success-coloured elements. |
318
+ | `--df-color-warning` | `#fbb34e` | Warning colour. |
319
+ | `--df-color-warning-darker` | `#fea429` | Darker warning variant. |
320
+ | `--df-color-warning-text` | `#ffffff` | Text on warning-coloured elements. |
321
+ | `--df-color-neutral` | `#6c757d` | Neutral colour. |
322
+ | `--df-color-neutral-darker` | `#5a6268` | Darker neutral variant. |
323
+ | `--df-color-neutral-text` | `#ffffff` | Text on neutral-coloured elements. |
324
+ | `--df-color-error` | `#dc3545` | Error / danger colour. |
325
+ | `--df-color-error-lighter` | `#fa5969` | Lighter error variant. |
326
+ | `--df-color-error-text` | `#721c24` | Text on error-coloured elements. |
327
+
328
+ ### Status / Tolerance Colours
329
+
330
+ These control how fields are highlighted when their values are within or outside
331
+ the defined tolerance range.
332
+
333
+ | Variable | Default | Description |
334
+ |---|---|---|
335
+ | `--df-color-required-star` | `#f98989` | Colour of the asterisk on required fields. |
336
+ | `--df-color-within-tolerance-border` | `#8fff8f` | Border colour when field value is within tolerance. |
337
+ | `--df-color-within-tolerance-background` | `#b9fb9f` | Background when field value is within tolerance. |
338
+ | `--df-color-within-tolerance-input-text` | `#214521` | Text colour in input when within tolerance. |
339
+ | `--df-color-within-tolerance-text` | `#3c763d` | Label/message text when within tolerance. |
340
+ | `--df-color-out-of-tolerance-border` | `#fdf8b2` | Border colour when field value is out of tolerance. |
341
+ | `--df-color-out-of-tolerance-text` | `#d9534f` | Text/label colour when out of tolerance. |
342
+ | `--df-color-has-error-border` | *(inherits error colour)* | Border colour when field has a validation error. |
343
+ | `--df-color-has-error-background` | `#d58d8b` | Background when field has a validation error. |
344
+
345
+ ### Layout
346
+
347
+ | Variable | Default | Description |
348
+ |---|---|---|
349
+ | `--df-border-radius` | `4px` | Corner radius for inputs, sections, and the component itself. |
350
+ | `--df-spacing-medium` | `1rem` | Medium spacing unit used for padding/gaps. |
351
+ | `--df-spacing-large` | `1.5rem` | Large spacing unit used for section padding. |
352
+ | `--df-button-padding` | `0.3rem 1rem` | Padding applied to Shoelace `<sl-button>` elements. |
353
+
354
+ ### Example: Dark Theme
355
+
356
+ ```css
357
+ dynamic-form {
358
+ --df-color-background: #1e1e2e;
359
+ --df-color-background-section: #2a2a3e;
360
+ --df-color-background-input-base: #2d2d42;
361
+ --df-color-text: #cdd6f4;
362
+ --df-color-text-label: #cdd6f4;
363
+ --df-color-input-text: #cdd6f4;
364
+ --df-color-border: #45475a;
365
+ --df-color-accent: #89b4fa;
366
+ --df-color-accent-darker: #74a0e8;
367
+ --df-color-accent-text: #1e1e2e;
368
+ }
369
+ ```
370
+
371
+ ---
372
+
373
+ ## Contributing / Building from Source
374
+
375
+ ### Prerequisites
376
+
377
+ - Python ≥ 3.14 (with [uv](https://docs.astral.sh/uv/))
378
+ - Node.js ≥ 20 with npm
379
+
380
+ ### Build
381
+
382
+ Use the included `build.sh` to produce a distributable wheel and sdist:
383
+
384
+ ```bash
385
+ # Build both sdist and wheel (default)
386
+ ./build.sh
387
+
388
+ # Build wheel only
389
+ ./build.sh --wheel
390
+
391
+ # Build sdist only
392
+ ./build.sh --sdist
393
+ ```
394
+
395
+ The script will:
396
+
397
+ 1. Run `npm run build` inside `kpforms/kpforms/` to compile the TypeScript
398
+ web components and copy the output into `kpforms/static/`.
399
+ 2. Run `python manage.py makemigrations kpforms` to ensure migrations are
400
+ up-to-date.
401
+ 3. Run `uv build` to produce the final packages in `dist/`.
402
+
403
+ ### Running Tests
404
+
405
+ ```bash
406
+ uv run pytest
407
+ ```
408
+
409
+ ---
410
+
411
+ ## Licence
412
+
413
+ MIT — see [LICENSE](../LICENSE) for details.
414
+