I'm a new frontend programmer.
I'm making a web site with flask, and to be honest, I don't know much about front-end so I don't know if what I'm doing is best practices. In one section, I have a div containing other divs with a table.
My concern is if I'm using the right structure for that, so that's why I'm asking people with more expertise in this field. I changed some things so it would be pureHTML, CSS and JS, but the data in the table appears dynamically if there is some data in the data base.
function validate() {
// check if input is bigger than 3
var value = document.getElementById("expense-name").value;
if (value.length < 1) {
return false; // keep form from submitting
}
// else form is good let it submit, of course you will
// probably want to alert the user WHAT went wrong.
return true;
}
body{
background-color: bisque;
font-family: Arial, Helvetica, sans-serif;
}
/* index.html */
/* Container to center the div container */
.super-container{
display: flex;
justify-content: center;
align-items: center;
}
/* Centering the container */
.container{
display: flex;
overflow: hidden;
flex-direction: column;
justify-content: center;
align-items: center;
align-content: flex-start;
box-sizing: border-box;
padding: 1%;
min-height: 30vh;
width: 78vw;
background-color: white;
}
/* Deletes the arrows of inputs fields */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
/* display: none; <- Crashes Chrome on hover */
-webkit-appearance: none;
margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
}
input[type=number] {
appearance:textfield; /* Firefox */
}
/* Adding border, padding, and font size of caption to table*/
.container h2{
padding: 10px 0;
font-size: 25px;
font-weight: 600;
}
.container td{
border: 1px groove rgba(0, 0, 0, 0.178);
box-sizing: border-box;
background-color: white;
text-align: center;
width: 40%;
padding: 5px;
}
.container table{
margin: 10px auto;
height: 50%;
border: 2px groove rgba(0, 0, 0, 0.178);
border-collapse: collapse;
width: 70%
}
.container th{
border: 1px groove rgba(236, 0, 0, 0.178);
background-color: whitesmoke;
padding: 10px 45px;
box-sizing: border-box;
width: 50%;
}
<div class="super-container">
<div class="container">
<h2>Expense Tracker</h2>
<form action="/" method="post">
<input type="text" name="name" placeholder="test" required />
<input type="number" name="amount" placeholder="Amount" required />
<select name="category" id="">
<option>Opcion 1</option>
</select>
<input type="date" name="date" />
<input type="submit" name="" id="" />
</form>
<table>
<tr>
<th>Expense Name</th>
<th>Amount</th>
<th>Category</th>
<th>Date</th>
<th>Action</th>
</tr>
<tr>
<td>Test1</td>
<td>Test1</td>
<td>Test1</td>
<td>Test1</td>
<td>
<form
action="{{ url_for('delete_item', id=expense.id)}}"
method="POST"
>
<input type="submit" value="Delete" />
</form>
</td>
</tr>
</table>
</div>
</div>
1 Answer 1
What better to improve:
Semantics & accessibility
Use main/section, a single page heading (
h1
), and proper table structure:caption
,thead
,tbody
, andth scope="col"
.Every input should have a
<label for="...">
. This helps screen-readers and improves click targets.Give the Action column an accessible header (visually hidden text).
Forms
Your "delete" form per row is fine if you also include CSRF protection (Flask-WTF or your own token).
Alternative: one outer form and send the clicked row id via a hidden input—either pattern is OK; don’t nest forms.
Validation
HTML5 constraints (
required
,minlength
,min
,step
) already cover yourvalidate()
use-case. Keep JS only for custom messages or async checks.Layout/CSS
Prefer a centered container with
max-width
+margin: 0 auto
over 78vw.Use gap with CSS Grid/Flex for spacing instead of extra wrappers.
Tables: add
table-layout: fixed;
for stable column widths.
html
<main class="container">
<h1>Expense Tracker</h1>
<form action="{{ url_for('create_expense') }}" method="post" class="form">
{{ csrf_token() }}
<div class="grid">
<label for="expense-name">Name
<input id="expense-name" name="name" type="text" required minlength="1" maxlength="100">
</label>
<label for="amount">Amount
<input id="amount" name="amount" type="number" required min="0" step="0.01" inputmode="decimal">
</label>
<label for="category">Category
<select id="category" name="category" required>
<option value="" disabled selected>Select...</option>
<option value="option-1">Option 1</option>
</select>
</label>
<label for="date">Date
<input id="date" name="date" type="date" required>
</label>
<button type="submit" class="btn">Add expense</button>
</div>
</form>
<table class="table">
<caption class="sr-only">List of expenses</caption>
<thead>
<tr>
<th scope="col">Expense Name</th>
<th scope="col">Amount</th>
<th scope="col">Category</th>
<th scope="col">Date</th>
<th scope="col"><span class="sr-only">Actions</span></th>
</tr>
</thead>
<tbody>
{% for expense in expenses %}
<tr data-id="{{ expense.id }}">
<td>{{ expense.name }}</td>
<td>{{ expense.amount }}</td>
<td>{{ expense.category }}</td>
<td>{{ expense.date }}</td>
<td>
<form action="{{ url_for('delete_item', id=expense.id) }}" method="post">
{{ csrf_token() }}
<button type="submit" class="btn btn-danger">Delete</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</main>
css
/* base */
*,
*::before,
*::after { box-sizing: border-box; }
body {
margin: 0;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
line-height: 1.5;
background: #f7efe3; /* bisque-like */
color: #222;
}
/* layout */
.container {
max-width: 960px;
margin: 2rem auto;
padding: 1.25rem;
background: #fff;
border-radius: .5rem;
box-shadow: 0 1px 4px rgba(0,0,0,.06);
}
/* form */
.form .grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: .75rem 1rem;
align-items: end;
}
label { display: grid; gap: .25rem; font-weight: 600; }
input, select, button {
font: inherit;
padding: .5rem .6rem;
border: 1px solid #d0d0d0;
border-radius: .4rem;
}
.btn { cursor: pointer; background:#0d6efd; color:#fff; border:1px solid #0d6efd; }
.btn:hover { filter: brightness(.95); }
.btn-danger { background:#d9534f; border-color:#d9534f; }
/* table */
.table {
width: 100%;
margin-top: 1rem;
border-collapse: collapse;
table-layout: fixed;
}
.table th, .table td {
padding: .6rem .75rem;
border: 1px solid rgba(0,0,0,.12);
text-align: left;
word-break: break-word;
}
.table thead th { background: #f5f5f5; }
/* a11y: hide visually, keep for screen-readers */
.sr-only {
position: absolute !important;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden; clip: rect(0,0,0,0);
white-space: nowrap; border: 0;
}
validate()
get called? Is it connected to the<form>
or another element(s) viaaddEventListener()
,on<Event>
properties or something similar? \$\endgroup\$