CSS (forms) refactoring (#13)
CSS (forms) refactoring * All forms now use the render_field helper. * CSS is now compiled using PostCSS. * Common form styles got extracted to more reusable & flexible classes. Closes #12. Co-authored-by: Wojciech Kwolek <wojciech@kwolek.xyz> Reviewed-on: https://git.r23s.eu/wojciech/doneth.at-backend/pulls/13
This commit is contained in:
parent
b1a34784ae
commit
716055cf6a
|
|
@ -0,0 +1,21 @@
|
|||
.accomplishment .text {
|
||||
@apply flex-grow;
|
||||
}
|
||||
|
||||
.accomplishment .difficulty {
|
||||
@apply flex-shrink-0 pl-1 font-bold text-right;
|
||||
min-width: 3em;
|
||||
}
|
||||
|
||||
.difficulty-easy {
|
||||
@apply text-green-700;
|
||||
}
|
||||
|
||||
.difficulty-medium {
|
||||
@apply text-orange-700;
|
||||
}
|
||||
|
||||
.difficulty-hard {
|
||||
@apply text-red-700;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
.section {
|
||||
@apply w-full;
|
||||
@apply max-w-lg;
|
||||
@apply m-auto;
|
||||
&.section-wider {
|
||||
@apply max-w-xl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.card {
|
||||
@apply px-8;
|
||||
@apply pt-6;
|
||||
@apply pb-6;
|
||||
@apply my-4;
|
||||
@apply bg-white;
|
||||
@apply rounded;
|
||||
@apply shadow-md;
|
||||
}
|
||||
|
||||
|
||||
.link {
|
||||
@apply text-blue-700;
|
||||
@apply underline;
|
||||
|
||||
&:hover {
|
||||
@apply text-blue-500;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
.form {
|
||||
@apply w-full;
|
||||
@apply mx-auto;
|
||||
&.form-narrow {
|
||||
@apply max-w-xs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.btn, input:not([type=submit]), select {
|
||||
@apply shadow;
|
||||
@apply appearance-none;
|
||||
@apply outline-none;
|
||||
@apply rounded;
|
||||
@apply py-2;
|
||||
@apply px-3;
|
||||
@apply block;
|
||||
@apply text-gray-700;
|
||||
@apply leading-tight;
|
||||
@apply transition-all;
|
||||
@apply duration-75;
|
||||
|
||||
&:focus {
|
||||
@apply shadow-outline;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
input:not([type=submit]), select {
|
||||
@apply w-full;
|
||||
/* rounded borders look weird on buttons in firefox, so we only apply that here */
|
||||
@apply border-gray-300;
|
||||
@apply border;
|
||||
@apply mx-auto;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.input[type=submit] {
|
||||
@apply bg-none;
|
||||
}
|
||||
|
||||
.btn, input[type="submit"] {
|
||||
@apply border-none;
|
||||
|
||||
&.btn-blue {
|
||||
@apply text-white;
|
||||
@apply bg-blue-600;
|
||||
&:hover { @apply bg-blue-800; }
|
||||
&:focus { @apply bg-blue-900; }
|
||||
}
|
||||
|
||||
&.btn-green {
|
||||
@apply text-white;
|
||||
@apply bg-green-500;
|
||||
&:hover { @apply bg-green-600; }
|
||||
&:focus { @apply bg-green-700; }
|
||||
}
|
||||
|
||||
&.btn-orange {
|
||||
@apply text-white;
|
||||
@apply bg-orange-500;
|
||||
&:hover { @apply bg-orange-600; }
|
||||
&:focus { @apply bg-orange-700; }
|
||||
}
|
||||
|
||||
&.btn-red {
|
||||
@apply text-white;
|
||||
@apply bg-red-500;
|
||||
&:hover { @apply bg-red-600; }
|
||||
&:focus { @apply bg-red-700; }
|
||||
}
|
||||
|
||||
&.btn-wide {
|
||||
@apply px-6;
|
||||
}
|
||||
|
||||
&.btn-inline {
|
||||
@apply inline-block;
|
||||
}
|
||||
|
||||
|
||||
&.btn-center { @apply mx-auto; }
|
||||
}
|
||||
|
||||
.field.error input {
|
||||
@apply border-red-500;
|
||||
}
|
||||
|
||||
133
app/css/main.css
133
app/css/main.css
|
|
@ -1,126 +1,21 @@
|
|||
@tailwind base;
|
||||
@import "tailwindcss/base";
|
||||
@import "tailwindcss/components";
|
||||
@import "tailwindcss/utilities";
|
||||
|
||||
@tailwind components;
|
||||
|
||||
@tailwind utilities;
|
||||
@import 'accomplishments.css';
|
||||
@import 'common.css';
|
||||
@import 'forms.css';
|
||||
|
||||
body {
|
||||
@apply bg-gray-200;
|
||||
}
|
||||
|
||||
form input[type=text],
|
||||
form input[type=password],
|
||||
form input[type=number],
|
||||
form select {
|
||||
@apply shadow;
|
||||
@apply appearance-none;
|
||||
@apply border;
|
||||
@apply rounded;
|
||||
@apply w-full;
|
||||
@apply py-2;
|
||||
@apply px-3;
|
||||
@apply text-gray-700;
|
||||
@apply leading-tight;
|
||||
}
|
||||
|
||||
form input:focus,
|
||||
form select:focus {
|
||||
@apply outline-none;
|
||||
@apply shadow-outline;
|
||||
}
|
||||
|
||||
form div.error input {
|
||||
@apply border-red-500;
|
||||
}
|
||||
|
||||
form div.error ul.errors {
|
||||
@apply pt-1 pl-1;
|
||||
}
|
||||
|
||||
form div.error ul.errors li {
|
||||
@apply text-xs italic text-red-500;
|
||||
}
|
||||
|
||||
form input[type=text]:focus,
|
||||
form input[type=password]:focus {
|
||||
@apply outline-none;
|
||||
@apply shadow-outline;
|
||||
}
|
||||
|
||||
form input[type=submit] {
|
||||
@apply px-4 py-2 mt-2 font-bold rounded;
|
||||
}
|
||||
|
||||
form.auth-form input[type=submit] {
|
||||
@apply w-full text-white bg-blue-500;
|
||||
}
|
||||
|
||||
.green-btn {
|
||||
@apply text-white bg-green-500;
|
||||
}
|
||||
|
||||
.accomplishment .text {
|
||||
@apply flex-grow;
|
||||
}
|
||||
|
||||
.accomplishment .difficulty {
|
||||
@apply flex-shrink-0 pl-1 font-bold text-right;
|
||||
min-width: 3em;
|
||||
}
|
||||
|
||||
.difficulty-easy {
|
||||
@apply text-green-700;
|
||||
}
|
||||
|
||||
.difficulty-medium {
|
||||
@apply text-orange-700;
|
||||
}
|
||||
|
||||
.difficulty-hard {
|
||||
@apply text-red-700;
|
||||
}
|
||||
|
||||
.green-btn:hover {
|
||||
@apply bg-green-700;
|
||||
}
|
||||
|
||||
.orange-btn {
|
||||
@apply text-white bg-orange-500;
|
||||
}
|
||||
|
||||
.orange-btn:hover {
|
||||
@apply bg-orange-700;
|
||||
}
|
||||
|
||||
.red-btn {
|
||||
@apply text-white bg-red-500;
|
||||
}
|
||||
|
||||
.red-btn:hover {
|
||||
@apply bg-red-700;
|
||||
}
|
||||
|
||||
|
||||
form.auth-form input[type=submit]:hover {
|
||||
@apply bg-blue-700;
|
||||
}
|
||||
|
||||
form input[type=submit]:focus {
|
||||
@apply shadow-outline outline-none;
|
||||
}
|
||||
|
||||
.link {
|
||||
@apply text-blue-700 underline;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
@apply text-blue-500;
|
||||
}
|
||||
|
||||
.card {
|
||||
@apply px-8 pt-6 pb-6 mt-4 mb-4 bg-white rounded shadow-md;
|
||||
}
|
||||
|
||||
.auth-form.card {
|
||||
@apply pb-4;
|
||||
.screencap {
|
||||
@apply overflow-hidden;
|
||||
@apply border-2;
|
||||
@apply border-black;
|
||||
@apply border-gray-500;
|
||||
@apply border-solid;
|
||||
@apply rounded-lg;
|
||||
@apply shadow-xl;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
{% macro render_field(field, label=True, wrapper_class="", description="") %}
|
||||
<div class="mb-4 {% if field.errors %}error{% endif %} {{ wrapper_class }}">
|
||||
<label for="{{ field.id }}"
|
||||
class="block text-sm font-bold text-gray-700">{% if label %}{{ field.label }}{% endif %}</label>
|
||||
{% macro render_field(field, label=True, mb="mb-4", wrapper_class="", description="") %}
|
||||
<div class="field {% if field.errors %}error{% endif %} {% if mb %} {{ mb }} {% endif %} {{ wrapper_class }}">
|
||||
{% if label %}
|
||||
<label for="{{ field.id }}" class="block text-sm font-bold text-gray-700">{{ field.label }}</label>
|
||||
<div class="mb-2 text-xs text-gray-700">{{ description }}</div>
|
||||
{% endif %}
|
||||
{% if field.type == "SelectField" %}
|
||||
<div class="relative">
|
||||
{{ field(**kwargs)|safe }}
|
||||
|
|
@ -15,7 +16,7 @@
|
|||
{{ field(**kwargs)|safe }}
|
||||
{% endif %}
|
||||
{% if field.errors %}
|
||||
<ul class="errors">
|
||||
<ul class="pl-1 mt-2 text-xs text-red-700 errors">
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,12 @@
|
|||
<span class="pl-1"><a class="text-xs link" href="{{ url_for('auth.logout') }}">Log
|
||||
out</a></span>
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
<span class="pr-1"><a class="text-xs link" href="{{ url_for('main.index') }}">Home</a></span>
|
||||
<span class="px-1"><a class="text-xs link" href="{{ url_for('auth.login') }}">Log in</a></span>
|
||||
<span class="pl-1"><a class="text-xs link" href="{{ url_for('auth.register') }}">Register</a></span>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
{% block title %}Log in{% endblock %}
|
||||
{% from "_formhelpers.html" import render_field %}
|
||||
{% block content %}
|
||||
<div class="w-full max-w-lg mx-auto card">
|
||||
<form method="POST" class="w-full max-w-xs mx-auto auth-form">
|
||||
<div class="section card">
|
||||
<form method="POST" class="form form-narrow">
|
||||
{{ form.csrf_token }}
|
||||
{{ render_field(form.username) }}
|
||||
{{ render_field(form.password) }}
|
||||
{{ render_field(form.submit, False) }}
|
||||
{{ render_field(form.submit, False, class_="btn btn-blue btn-wide btn-center") }}
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
{% block title %}Log out{% endblock %}
|
||||
{% from "_formhelpers.html" import render_field %}
|
||||
{% block content %}
|
||||
<div class="w-full max-w-lg mx-auto card">
|
||||
<form method="POST" class="w-full max-w-xs mx-auto auth-form">
|
||||
<div class="section card">
|
||||
<form method="POST" class="form-narrow form">
|
||||
<h3 class="text-center">Are you sure you want to log out?</h3>
|
||||
{{ form.csrf_token }}
|
||||
{{ render_field(form.submit, False) }}
|
||||
{{ render_field(form.submit, False, mb="mb-2", class="mt-3 btn btn-red btn-wide btn-center") }}
|
||||
<p class="text-xs text-center">
|
||||
<a class="link" href="{{ url_for('main.index') }}">cancel</a>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
{% block title %}Register{% endblock %}
|
||||
{% from "_formhelpers.html" import render_field %}
|
||||
{% block content %}
|
||||
<div class="w-full max-w-lg mx-auto card">
|
||||
<form method="POST" class="w-full max-w-xs mx-auto auth-form">
|
||||
<div class="section card">
|
||||
<form method="POST" class="form form-narrow">
|
||||
{{ form.csrf_token }}
|
||||
{{ render_field(form.username) }}
|
||||
{{ render_field(form.tz) }}
|
||||
{{ render_field(form.password) }}
|
||||
{{ render_field(form.confirm) }}
|
||||
{{ render_field(form.submit, False) }}
|
||||
{{ render_field(form.submit, False, class_="btn btn-blue btn-wide btn-center") }}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -14,15 +14,15 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div class="max-w-xl m-auto overflow-hidden border-2 border-black border-gray-500 border-solid rounded-lg shadow-xl">
|
||||
<div class="screencap section section-wider">
|
||||
<img src="{{ static_url_for('static', filename='screencap.png') }}">
|
||||
</div>
|
||||
|
||||
<div class="max-w-lg m-auto text-center card">
|
||||
<div class="text-center section card">
|
||||
<h2 class="mb-4 text-2xl">Interested?</h2>
|
||||
<p class="mb-4">
|
||||
<a href="{{ url_for('auth.register') }}" class="px-4 py-2 text-white bg-blue-700 rounded hover:bg-blue-500">
|
||||
Sign up</a> or <a href="{{ url_for('auth.login') }}" class="link">log in</a>.
|
||||
<a href="{{ url_for('auth.register') }}" class="btn btn-blue btn-inline">
|
||||
Sign up</a> <span class="px-1">or</span> <a href="{{ url_for('auth.login') }}" class="link">log in</a>.
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
{% extends "_skel.html" %}
|
||||
{% from "_formhelpers.html" import render_field %}
|
||||
{% block title %}{{ day.pretty }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="max-w-xl mx-auto card">
|
||||
<form method="POST" action="{{ url_for('main.index') }}">
|
||||
<form method="POST" action="{{ url_for('main.index') }}" class="form">
|
||||
{{ form.csrf_token }}
|
||||
{{ form.text(placeholder="What did you accomplish today?", class_="placeholder-black", autofocus=True) }}
|
||||
<div class="flex mt-1">
|
||||
{{ form.submit_5(class_="w-1/3 mr-1 green-btn") }}
|
||||
{{ form.submit_10(class_="w-1/3 mx-1 orange-btn") }}
|
||||
{{ form.submit_15(class_="w-1/3 ml-1 red-btn") }}
|
||||
{{ render_field(form.text, label=False, mb=False, placeholder="What did you accomplish today?", class_="placeholder-black w-full", autofocus=True) }}
|
||||
<div class="flex mt-3">
|
||||
{{ form.submit_5(class_="w-1/3 mr-1 btn btn-green") }}
|
||||
{{ form.submit_10(class_="w-1/3 mx-1 btn btn-orange") }}
|
||||
{{ form.submit_15(class_="w-1/3 ml-1 btn btn-red") }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
{% extends "_skel.html" %}
|
||||
{% from "_formhelpers.html" import render_field %}
|
||||
{% block title %}Delete accomplishment{% endblock %}
|
||||
{% block content %}
|
||||
<div class="max-w-xl mx-auto text-center card">
|
||||
<div class="text-center section card">
|
||||
<p class="italic">
|
||||
{{ accomplishment.text }}
|
||||
</p>
|
||||
|
|
@ -9,12 +10,12 @@
|
|||
XP)
|
||||
</p>
|
||||
</div>
|
||||
<div class="max-w-lg mx-auto text-center card">
|
||||
<div class="text-center section card">
|
||||
<h3 class="text-lg">Are you sure you want to remove this accomplishment?</h3>
|
||||
<form class="mt-2" method="POST">
|
||||
<form class="mt-3" method="POST">
|
||||
{{ form.csrf_token }}
|
||||
{{ form.submit(class_="hover:bg-red-500 bg-red-700 text-white") }}
|
||||
<a href="{{ cancel }}" class="block mt-2 text-sm text-blue-700 hover:text-blue-500">cancel</a>
|
||||
{{ render_field(form.submit, label=False, mb="mb-2", class_="btn btn-red btn-wide btn-center") }}
|
||||
<a href="{{ cancel }}" class="block text-xs link">cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -3,20 +3,22 @@
|
|||
{% from "_formhelpers.html" import render_field %}
|
||||
{% block content %}
|
||||
{% if day %}
|
||||
<div class="w-full max-w-lg mx-auto card">
|
||||
<div class="section card">
|
||||
<div class="text-xl text-center">
|
||||
You're adding an accomplishment made on {{ day.pretty }}.
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<form method="POST" class="w-full max-w-lg mx-auto card" {% if day %}action="{{ url_for('main.add_day', day=day.url) }}"
|
||||
<div class="section card">
|
||||
<form method="POST" class="form form-narrow" {% if day %}action="{{ url_for('main.add_day', day=day.url) }}"
|
||||
{% endif %}>
|
||||
{{ form.csrf_token }}
|
||||
{{ render_field(form.text) }}
|
||||
{{ render_field(form.difficulty) }}
|
||||
{{ render_field(form.difficulty, mb="mb-6") }}
|
||||
<div class="text-center">
|
||||
{{ render_field(form.submit, False, class_="bg-blue-700 text-white hover:bg-blue-500") }}
|
||||
<a href="{{ cancel }}" class="block mt-2 text-sm text-blue-700 hover:text-blue-500">cancel</a>
|
||||
{{ render_field(form.submit, False, mb="mb-1", class_="btn btn-blue btn-center btn-wide") }}
|
||||
<a href="{{ cancel }}" class="text-xs link">cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
{% block title %}Settings{% endblock %}
|
||||
{% from "_formhelpers.html" import render_field %}
|
||||
{% block content %}
|
||||
<div class="w-full max-w-lg mx-auto card">
|
||||
<div class="section card">
|
||||
<!-- TODO: this shouldn't use the auth-form class, that class should be generalized -->
|
||||
<form method="POST" class="max-w-xs mx-auto auth-form">
|
||||
<form method="POST" class="form form-narrow">
|
||||
{{ form.csrf_token }}
|
||||
{{ render_field(form.timezone) }}
|
||||
{{ render_field(form.start_of_day, description="This is useful if you often work after midnight. If you set it to e.g. 2, all accomplishments made before 2 AM will be considered to be made on the previous day.") }}
|
||||
{{ render_field(form.submit, False) }}
|
||||
{{ render_field(form.submit, False, class_="btn btn-blue btn-wide btn-center") }}
|
||||
{% if success %}<p class="text-center text-green-800">Saved successfuly.</p>{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"cssnano": "^4.1.10",
|
||||
"postcss-cli": "^8.0.0",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-nested": "4.2.3",
|
||||
"tailwindcss": "^1.7.5"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tailwindcss build app/css/main.css -o app/static/style.css"
|
||||
"build": "postcss app/css/main.css --output app/static/style.css"
|
||||
},
|
||||
"name": "donethat-backend",
|
||||
"version": "0.0.1",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
const plugins = [
|
||||
require('postcss-import'),
|
||||
require('tailwindcss'),
|
||||
require('postcss-nested'),
|
||||
require('autoprefixer')
|
||||
]
|
||||
|
||||
if (process.env.NODE_ENV == "production") {
|
||||
plugins.push(require('cssnano')({
|
||||
preset: 'default',
|
||||
}))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
plugins
|
||||
}
|
||||
Loading…
Reference in New Issue