Layout
How HTML pieces come together to form Dpgstack UI.
Properties: Layout parts
Base — contains all JS scripts in head, default attributes for body, as well as optional notifications and sidebar:
{% load static tailwind_tags %} <!DOCTYPE html> <html lang="en"> <meta name="viewport" content="width=device-width, initial-scale=1" /> <head> <script src="{% static 'js/darkmode.js' %}" defer></script> <script src="{% static 'js/notifications.js' %}" defer></script> <script src="{% static 'js/flatpickr.min.js' %}" defer></script> <script src="{% static 'js/dpg_flatpickr.js' %}" defer></script> <script src="{% static 'js/htmx.min.js' %}" defer></script> <script src="{% static 'js/htmx.sse.js' %}" defer></script> <script defer src="{% static 'js/alpine.focus.js' %}"></script> <script defer src="{% static 'js/alpine.collapse.js' %}"></script> <script defer src="{% static 'js/alpine.mask.js' %}"></script> <script src="{% static 'js/alpine.min.js' %}" defer></script> {% tailwind_css %} <link rel="stylesheet" type="text/css" href="{% static 'css/styles.css' %}" /> <link rel="stylesheet" type="text/css" href="{% static 'css/flatpickr.min.css' %}" /> <title>my site</title> </head> <body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}' {{ attrs }} class="bg-surface dark:bg-surface-dark font-inter text-on-surface dark:text-on-surface-dark overflow-hidden transition-colors duration-300"> <c-fancy.notifications /> <c-fancy.sidebar.sidebar> {{ slot }} </c-fancy.sidebar.sidebar> </body> </html>
Card layout grid
<div class="h-screen flex">
<div class="flex-1 overflow-y-auto p-6">
<div class="grid grid-cols-3 sm:grid-cols-4 lg:grid-cols-5 gap-4 p-4">
{% for piece in queryset %}<c-fancy.card />{% endfor %}
</div>
</div>
</div>
Full-height scrollable tables without pixel offsets
How to make tables fill the remaining viewport height without using h-screen, calc(), or any other fixed pixel offsets.
Workflow: Converting a table from fixed height to flex-column height
Identify the layout chain from
bodydown to the scrollable table wrapper.Confirm
body/base hasoverflow-hidden— this is required for the chain to work.Confirm the content area (
#main-contentor equivalent) hash-svworh-screen— this anchors the chain.Add
h-fullto any intermediate wrapper<div>that sits between the content area and the table.On the outer table container (
#table-container), setclass="flex flex-col h-full".On the inner scrollable
<div>, replaceh-screenwithclass="flex-1 min-h-0 overflow-y-scroll".Remove any
h-[calc(...)]orh-screenclasses in the chain.
Properties: Height propagation rules
h-fullresolves to the parent’s computed height.h-svwresolves to exactly 100vh (dynamic viewport for mobile browsers).flex-1distributes remaining space in a flex container.min-h-0overrides the defaultmin-height: autoon flex children, allowing them to shrink below their content height. Required on the scrollable wrapper — without it, the table can’t shrink and overflows anyway.
Definition: Table layout chain (shoppingitem example)
for_table.html body
→ overflow-hidden
sidebar.html #main-content
→ h-svw (anchors the chain to viewport height)
shoppingitem.html flex row
→ h-full (propagates height through padding)
table_shopping.html #table-container
→ flex flex-col h-full
scroll div
→ flex-1 min-h-0 overflow-y-scroll
→ table
Each layer inherits height from the parent. No pixel values, no overflow, no guessing.
Workflow: Diagnosing a table that overflows bottom-of-screen
Open browser DevTools and inspect the scrollable
<div>.Check if its height is larger than the viewport minus the UI chrome (sidebar, header, padding).
If it uses
h-screenorh-[calc(100dvh-*)], note that those values are relative to the viewport, not the parent element.h-screendoes not account for parent padding, margins, or sibling elements. That’s the overflow source.Apply the conversion workflow above.