make-html
Build HTML from Python classes. Includes helpers for Bootstrap 4 and Font Awesome. Django integration is optional — the library works standalone.
Installation
pip install make-html
Imports
All classes and helpers are available directly from the top-level package:
from html_classes import HtmlElement, HtmlDiv, HtmlTable, HtmlBadge, font_awesome
Or import from submodules if preferred:
from html_classes.html import HtmlElement
from html_classes.bootstrap import HtmlBadge, HtmlAlert
from html_classes.font_awesome import font_awesome
Basic usage
from html_classes.html import HtmlElement
# Simple element
HtmlElement(contents='Hello', css_classes='my-class').render()
# → <div class="my-class">Hello</div>
# Change the tag
HtmlElement(element='span', contents='Hello').render()
# → <span>Hello</span>
# kwargs become HTML attributes; underscores convert to hyphens
HtmlElement(element='input', end_tag=False, type='text', data_value='1').render()
# → <input type="text" data-value="1"/>
# Self-closing tag
HtmlElement(element='br', end_tag=False).render()
# → <br/>
__str__ calls render(), so elements can be used directly in f-strings or nested inside other elements.
Building up content
from html_classes.html import HtmlElement, HtmlRoot
# append() or += to add children
el = HtmlElement(element='ul')
el.append(HtmlElement(element='li', contents='Item 1'))
el += HtmlElement(element='li', contents='Item 2')
# → <ul><li>Item 1</li><li>Item 2</li></ul>
# HtmlRoot renders children with no wrapping tag
root = HtmlRoot()
root += HtmlElement(element='h1', contents='Title')
root += HtmlElement(element='p', contents='Body')
root.render()
# → <h1>Title</h1><p>Body</p>
CSS classes
# String or list at init time
HtmlElement(css_classes='btn btn-primary')
HtmlElement(css_classes=['btn', 'btn-primary'])
# Add classes after construction
el = HtmlElement()
el.add_class('extra-class another-class')
Tooltips (Bootstrap)
HtmlElement(element='span', contents='?', tooltip='Help text').render()
# → <span data-tooltip="tooltip" data-original-title="Help text" data-html="true">?</span>
Convenience elements
Pre-built subclasses so you don't need to pass element= every time.
Text and inline
from html_classes.html import HtmlP, HtmlSpan, HtmlA, HtmlStrong, HtmlEm
from html_classes.html import HtmlH1, HtmlH2, HtmlH3, HtmlH4, HtmlH5, HtmlH6
HtmlP(contents='A paragraph.').render()
# → <p>A paragraph.</p>
HtmlSpan(contents='highlighted', css_classes='text-danger').render()
# → <span class="text-danger">highlighted</span>
HtmlA(contents='Click here', href='https://example.com').render()
# → <a href="https://example.com">Click here</a>
HtmlStrong(contents='Bold').render()
# → <strong>Bold</strong>
HtmlEm(contents='Italic').render()
# → <em>Italic</em>
HtmlH1(contents='Page title').render()
# → <h1>Page title</h1>
Lists
from html_classes.html import HtmlUl, HtmlOl, HtmlLi
ul = HtmlUl()
ul += HtmlLi(contents='First')
ul += HtmlLi(contents='Second')
ul.render()
# → <ul><li>First</li><li>Second</li></ul>
Media and void elements
from html_classes.html import HtmlImg, HtmlHr, HtmlBr
HtmlImg(src='photo.jpg', alt='A photo').render()
# → <img src="photo.jpg" alt="A photo"/>
HtmlHr().render()
# → <hr/>
HtmlBr().render()
# → <br/>
HtmlImg, HtmlHr, and HtmlBr have end_tag = False by default.
Forms
from html_classes.html import HtmlForm, HtmlButton, HtmlLabel, HtmlInput, HtmlSelect, HtmlOption, HtmlTextarea
HtmlForm(contents='...', method='post', action='/submit').render()
# → <form method="post" action="/submit">...</form>
HtmlButton(contents='Click me', css_classes='btn btn-primary').render()
# → <button class="btn btn-primary">Click me</button>
HtmlLabel(contents='Name', for_='name-input').render()
# → <label for="name-input">Name</label>
HtmlInput(type='text', name='username').render()
# → <input type="text" name="username"/>
HtmlSelect(contents='...', name='colour').render()
# → <select name="colour">...</select>
HtmlOption(contents='Red', value='red').render()
# → <option value="red">Red</option>
HtmlTextarea(contents='', name='bio', rows='4').render()
# → <textarea name="bio" rows="4"></textarea>
HtmlInput has end_tag = False by default, so it always renders as a self-closing tag.
Layout and semantic
from html_classes.html import HtmlDiv, HtmlNav, HtmlHeader, HtmlFooter, HtmlSection, HtmlMain
HtmlNav(contents='...', css_classes='navbar').render()
# → <nav class="navbar">...</nav>
HtmlHeader(contents='...').render()
# → <header>...</header>
HtmlFooter(contents='...').render()
# → <footer>...</footer>
HtmlSection(contents='...').render()
# → <section>...</section>
HtmlMain(contents='...').render()
# → <main>...</main>
HtmlDiv(contents='Hello', css_classes='container').render()
# → <div class="container">Hello</div>
Inline helpers
from html_classes.basic import html_i
html_i('Some italic text')
# → <i>Some italic text</i>
Returns the rendered string directly (not an HtmlElement instance).
Tables
from html_classes.html import HtmlTable, HtmlTr, HtmlTd
# Basic table — data is a list of rows, each row a list of cells
HtmlTable(data=[[1, 2], [3, 4]]).render()
# → <table><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></table>
# headers=N treats the first N rows as header rows (<th>)
HtmlTable(data=[['Name', 'Age'], ['Alice', 30]], headers=1).render()
# → <table><tr><th>Name</th><th>Age</th></tr><tr><td>Alice</td><td>30</td></tr></table>
# grouped=True wraps in <thead>/<tbody>
HtmlTable(data=[['Name', 'Age'], ['Alice', 30]], headers=1, grouped=True).render()
# → <table><thead><tr><th>Name</th><th>Age</th></tr></thead><tbody><tr><td>Alice</td><td>30</td></tr></tbody></table>
# left_headers=N treats the first N columns of body rows as <th>
HtmlTable(data=[['Name', 'Age'], ['Alice', 30]], headers=1, left_headers=1).render()
# → <table><tr><th>Name</th><th>Age</th></tr><tr><th>Alice</th><td>30</td></tr></table>
Column widths, styles, and classes
# col_widths sets fixed column widths and forces table-layout: fixed; width: 100%
HtmlTable(data=rows, headers=1, col_widths=['30%', '70%'])
# Per-column inline styles
HtmlTable(data=rows, col_styles=['text-align: right;', None])
# Per-column CSS classes
HtmlTable(data=rows, col_classes=['text-right', None])
Mixing HtmlTr / HtmlTd / HtmlTh in table data
Individual rows and cells can be HtmlTr / HtmlTd / HtmlTh instances for fine-grained control:
from html_classes.html import HtmlTr, HtmlTd, HtmlTh
# HtmlTr accepts a list of cell values and an optional cell_classes
HtmlTr(row_data=['a', 'b'], cell_classes='text-center').render()
# → <tr><td class="text-center">a</td><td class="text-center">b</td></tr>
# HtmlTd and HtmlTh work like HtmlElement with their tag pre-set
HtmlTd(contents='value', css_classes='highlight').render()
# → <td class="highlight">value</td>
HtmlTh(contents='Header').render()
# → <th>Header</th>
# Mix pre-built rows and cells with plain lists inside HtmlTable
HtmlTable(data=[
HtmlTr(['a', 'b']),
['c', HtmlTd('d', css_classes='highlight')],
])
Bootstrap 4 components
from html_classes.bootstrap import HtmlBadge, HtmlAlert
HtmlBadge(contents='New').render()
# → <span class="badge badge-primary">New</span>
HtmlBadge(contents='Warning', colour='warning').render()
# → <span class="badge badge-warning">Warning</span>
HtmlAlert(contents='Saved!', colour='success').render()
# → <div class="alert alert-success">Saved!</div>
Font Awesome
from html_classes.font_awesome import font_awesome
font_awesome('fas fa-question').render()
# → <i class="fas fa-question"></i>
Icon aliasing (Django)
Define a FONT_AWESOME_LIBRARY dict in Django settings to map short names to icon classes:
# settings.py
FONT_AWESOME_LIBRARY = {
'edit': 'fas fa-pen',
'delete': 'fas fa-trash',
}
font_awesome('edit') # → <i class="fas fa-pen"></i>
Or pass a library dict directly without Django:
font_awesome('edit', library={'edit': 'fas fa-pen'})
Extra kwargs are passed through as HTML attributes (e.g. for tooltips):
font_awesome('fas fa-info', tooltip='More info')
Subclassing
Subclass HtmlElement to create reusable components with fixed defaults:
from html_classes.html import HtmlElement
class MyButton(HtmlElement):
element = 'button'
default_classes = ['btn', 'btn-primary']
colour_class = 'btn-'
default_colour = 'primary'
default_classes— CSS classes applied when none are provided at initcolour_class— prefix prepended to thecolourvalue to form a class (e.g.colour_class='btn-'+colour='danger'→btn-danger)default_colour— fallback colour when none is passed
Metadata
Release files for make-html 0.0.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| make_html-0.0.3.tar.gz | 10.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| make_html-0.0.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 18.1 kB
Release files / make_html-0.0.3.tar.gz
| Download URL | make_html-0.0.3.tar.gz |
|---|---|
| Size | 10.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8d37a688d500c90d2c58d8ac4fa8a68deec91c27261ec88e9b5b990fde4e4be4
|
|
BLAKE2b-256 checksum How to use checksums |
e1c4795d54608015ec0052d6d78896ebd344fb677bb27f84638c36aca665fd72
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.0.1 CPython/3.13.0
|
Release files / make_html-0.0.3-py3-none-any.whl
| Download URL | make_html-0.0.3-py3-none-any.whl |
|---|---|
| Size | 7.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b9eed209de26585f9d89520c63a8a129ccaa6113d2993ef656433d3604bd5b70
|
|
BLAKE2b-256 checksum How to use checksums |
9f8d6e8fab6f1e4604b57487db3bca7c5ca9087f614ee4b47c270d0772b9eb10
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.0.1 CPython/3.13.0
|