Compare commits

...

23 Commits

Author SHA1 Message Date
efim
0af0129054 feat(18): not really working docker images 2024-03-15 06:49:46 +00:00
efim
8ff2ec3766 feat(18): displaying + for positive month comparison
wrapping + into span inline, to make it behave like part of
surrounding text in a div, somewhat ok
2024-03-14 14:10:23 +00:00
efim
843c71946f feat(18): desktop styling close enough 2024-03-14 13:51:32 +00:00
efim
51e3629fa5 feat(18): resize without transform, hover states
using transform influences text size, and size of rounded corners

current way hardcodes 8rem for the columns,
maybe i'll pass it as parameter when start doing desktop, i don't know
feels like not a good choice overall, but yeah
2024-03-14 13:04:30 +00:00
efim
ddc003eb13 feat(18): random input data 2024-03-13 17:23:32 +00:00
efim
e81a22dd10 feat(18): adding dynamic data 2024-03-13 17:08:03 +00:00
efim
269cb2967c feat(18): total this month part styled 2024-03-13 16:42:03 +00:00
efim
6b764ae61b fix(18): prettifying styles 2024-03-13 16:07:32 +00:00
efim
44c89e6559 feat(18): google fonts fix
maybe they stopped working without tracking?
2024-03-13 15:57:42 +00:00
efim
a541a2dac2 feat(18): spending bars curDay color 2024-03-13 15:25:25 +00:00
efim
ff7baa0a24 feat(18): initial spending columns component
tailwind doesn't seem to allow scale-y-[var(--the-var)]
maybe that's just the scale thing

overall, still setting style variables, this time with css blocks,
i suppose that's ok
2024-03-13 15:15:10 +00:00
efim
623d05da91 feat(18): basic templates 2024-03-13 12:58:26 +00:00
efim
8601288230 feat(18): colors and fonts to tailwind 2024-03-12 09:00:57 +00:00
efim
fa11926642 init: exercise expenses chart resources 2024-03-12 07:44:13 +00:00
efim
9c8ec7fb0d Add '18-expenses-chart/' from commit '78c9bd1d614c8bdd25a3e1d23bd0a39bb01e65f1'
git-subtree-dir: 18-expenses-chart
git-subtree-mainline: e13fa186e1
git-subtree-split: 78c9bd1d61
2024-03-12 07:09:35 +00:00
efim
78c9bd1d61 refactor: moving templates into dir
routes probably can live in main.go for simpler exercises
2024-03-12 05:46:57 +00:00
efim
9c19bd7b6b feat: initialized tailwind
basic conig targetting templ
templ html page with import
Makefile job that generates for the run
2024-03-11 10:45:47 +00:00
efim
de2fd2bdc0 feat: Makefile for local development
would be even more useful when i'll get tailwind and stuff
2024-03-11 07:32:28 +00:00
efim
81235e3ce6 feat: enabling templ 2024-03-11 04:44:55 +00:00
efim
4f7b2fcd17 init 2024-03-10 19:24:30 +00:00
efim
e13fa186e1 feat: preserving name \ region on active search
by using post - all inputs of same form are sent
so in active search scenario changing region also sends current name,
or changing name also sends current region

had to make additional post endpoint,
but with cask i can directly use form-value as function argument
and can reuse the code, yay
2023-10-11 03:22:31 +00:00
efim
f238940622 docs: readme for first Go exercise 2023-10-04 11:52:18 +00:00
efim
7f4b8cab8a feat: generating tailwind out.css in nix build 2023-10-04 11:28:28 +00:00
31 changed files with 852 additions and 893 deletions

View File

@@ -61,10 +61,10 @@
hx-get="/country"
hx-target="body"
hx-push-url="true"
class="md:w-1/4 md:min-w-max"
class="contents"
>
<div
class="flex flex-row items-center bg-white shadow-md h-14 md:h-16 rounded-lg md:w-full dark:bg-dark-blue"
class="md:w-1/4 md:min-w-max flex flex-row items-center bg-white shadow-md h-14 md:h-16 rounded-lg dark:bg-dark-blue"
>
<!-- <p class="w-16 grid place-content-center">IC</p> -->
<svg
@@ -87,11 +87,9 @@
name="countryName"
list="country.nameViews-list"
placeholder="Search for a country..."
th:hx-get="@{~/}"
hx-trigger="keyup changed delay:500ms"
hx-select="#countries-main-list"
hx-post="/countries-cards"
hx-target="#countries-main-list"
hx-swap="outerHTML"
hx-trigger="keyup changed delay:500ms"
/>
</div>
<datalist id="country.nameViews-list">
@@ -101,12 +99,10 @@
value=""
></option>
</datalist>
</form>
<form>
<select
name="region"
class="block w-62 mt-1 h-14 md:h-16 w-64 bg-white shadow-lg rounded-md shadow-sm focus:outline-none px-7 dark:bg-dark-blue"
hx-get="/countries-cards"
hx-post="/countries-cards"
hx-target="#countries-main-list"
>
<option

View File

@@ -64,6 +64,15 @@ case class Routes(countries: List[Country])(implicit
)
}
@cask.postForm("/countries-cards")
def postPageOfCountriesCards(
region: cask.FormValue,
countryName: cask.FormValue
) = {
println(s"in get for countries cards with $region and $countryName")
getPageOfCountriesCards(Some(region.value), 0, Some(countryName.value))
}
/** this method returns directly set of cards and new anchor for loading next
* page of cards
*
@@ -98,7 +107,7 @@ case class Routes(countries: List[Country])(implicit
context
)
// this is to store switch to another region in the history
val newUrl = s"/?region=${region.getOrElse("")}"
val newUrl = s"/?region=${region.getOrElse("")}&countryName=${countryName.getOrElse("")}"
// only save url when new region is requested, not on addtional card loads
val urlHeaderOpt = if (page == 0) Seq("HX-Push" -> newUrl) else Seq.empty
Response(
@@ -140,7 +149,7 @@ case class Routes(countries: List[Country])(implicit
}
@cask.get("/country")
def getCountryPage(countryName: String) = {
def getCountryPage(countryName: String, region: Option[String] = None) = {
val context = new Context()
countries.find(_.name.common == countryName) match {
case Some(selectedCountry) =>

View File

@@ -14,3 +14,5 @@
# Avoid your project being littered with annoying .DS_Store files!
.DS_Store
.prettierignore
public/out.css

View File

@@ -0,0 +1,96 @@
* Frontend Mentor - Results summary component solution
:PROPERTIES:
:CUSTOM_ID: frontend-mentor---results-summary-component-solution
:END:
This is a solution to the
[[https://www.frontendmentor.io/challenges/results-summary-component-CE_K6s0maV][Results
summary component challenge on Frontend Mentor]]. Frontend Mentor
challenges help you improve your coding skills by building realistic
projects.
** Overview
:PROPERTIES:
:CUSTOM_ID: overview
:END:
*** The challenge
:PROPERTIES:
:CUSTOM_ID: the-challenge
:END:
Users should be able to:
- View the optimal layout for the interface depending on their device's
screen size
- See hover and focus states for all interactive elements on the page
- *Bonus*: Use the local JSON data to dynamically populate the content
*** Screenshot
:PROPERTIES:
:CUSTOM_ID: screenshot
:END:
[[screenshot-mobile.png]]
[[screenshot-desktop.png]]
*** Links
:PROPERTIES:
:CUSTOM_ID: links
:END:
- Solution URL: https://github.com/efim/Learning-HTMX/tree/master/17-results-summary-component-go
- Live Site URL: https://efim-frontentmentor-results-summary-go.onrender.com/
** My process
:PROPERTIES:
:CUSTOM_ID: my-process
:END:
*** Built with
:PROPERTIES:
:CUSTOM_ID: built-with
:END:
- Semantic HTML5 markup
- TailwindCSS
- Flexbox
- CSS Grid
- Mobile-first workflow
- Go Server Side Rendering
- Nix for building and docker image creation
*** What I learned
:PROPERTIES:
:CUSTOM_ID: what-i-learned
:END:
**** restarting server of file watch
:PROPERTIES:
:CUSTOM_ID: restarting-server-of-file-watch
:END:
go doesn't have built in restart on file watch, and there are separate programs that do that
#+begin_src bash
wgo -verbose -file .go -file .gohtml -file tailwind.config.js tailwindcss -i ./input.css -o public/out.css :: go run main.go -p 1234
#+end_src
and doing tailwind first, because server embeds the output css file, so
it's needed for the build step
**** setting up static routes with default go web server
**** generating template and returning it as response of go route
**** embedding files into the binary for portability
**** using css variables to set custom colors in TailwindCSS
**** getting cli arguments in the go program
*** Continued development
:PROPERTIES:
:CUSTOM_ID: continued-development
:END:
Further things to learn: getting data from forms, general go things - coroutines, utilizing sqlite, embedding PocketBase
*** Useful resources
:PROPERTIES:
:CUSTOM_ID: useful-resources
:END:
- https://pkg.go.dev/text/template#example-Template-Block
main go templating documentation
- https://pkg.go.dev/net/http
main default server documentation
- https://nixos.wiki/wiki/Go - packaging Go
- https://golangdocs.com/command-line-arguments-in-golang
command line arguments

View File

@@ -7,6 +7,11 @@ in rec {
inherit pname version;
src = pkgs.nix-gitignore.gitignoreSource [ ] ./.;
vendorHash = null; # set to "" when get dependencies in go.mod
# Adding the Tailwind build step to preBuild
preBuild = ''
${pkgs.nodePackages.tailwindcss}/bin/tailwindcss -i ./input.css -o public/out.css
'';
};
image = pkgs.dockerTools.buildLayeredImage {
name = pname;

View File

@@ -1,855 +0,0 @@
/*
! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com
*/
/*
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
*/
*,
::before,
::after {
box-sizing: border-box;
/* 1 */
border-width: 0;
/* 2 */
border-style: solid;
/* 2 */
border-color: #e5e7eb;
/* 2 */
}
::before,
::after {
--tw-content: '';
}
/*
1. Use a consistent sensible line-height in all browsers.
2. Prevent adjustments of font size after orientation changes in iOS.
3. Use a more readable tab size.
4. Use the user's configured `sans` font-family by default.
5. Use the user's configured `sans` font-feature-settings by default.
6. Use the user's configured `sans` font-variation-settings by default.
*/
html {
line-height: 1.5;
/* 1 */
-webkit-text-size-adjust: 100%;
/* 2 */
-moz-tab-size: 4;
/* 3 */
-o-tab-size: 4;
tab-size: 4;
/* 3 */
font-family: HankenGrotesk, sans-serif;
/* 4 */
font-feature-settings: normal;
/* 5 */
font-variation-settings: normal;
/* 6 */
}
/*
1. Remove the margin in all browsers.
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
*/
body {
margin: 0;
/* 1 */
line-height: inherit;
/* 2 */
}
/*
1. Add the correct height in Firefox.
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
3. Ensure horizontal rules are visible by default.
*/
hr {
height: 0;
/* 1 */
color: inherit;
/* 2 */
border-top-width: 1px;
/* 3 */
}
/*
Add the correct text decoration in Chrome, Edge, and Safari.
*/
abbr:where([title]) {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
}
/*
Remove the default font size and weight for headings.
*/
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
/*
Reset links to optimize for opt-in styling instead of opt-out.
*/
a {
color: inherit;
text-decoration: inherit;
}
/*
Add the correct font weight in Edge and Safari.
*/
b,
strong {
font-weight: bolder;
}
/*
1. Use the user's configured `mono` font family by default.
2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp,
pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
/* 1 */
font-size: 1em;
/* 2 */
}
/*
Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/*
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/*
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
3. Remove gaps between table borders by default.
*/
table {
text-indent: 0;
/* 1 */
border-color: inherit;
/* 2 */
border-collapse: collapse;
/* 3 */
}
/*
1. Change the font styles in all browsers.
2. Remove the margin in Firefox and Safari.
3. Remove default padding in all browsers.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
/* 1 */
font-size: 100%;
/* 1 */
font-weight: inherit;
/* 1 */
line-height: inherit;
/* 1 */
color: inherit;
/* 1 */
margin: 0;
/* 2 */
padding: 0;
/* 3 */
}
/*
Remove the inheritance of text transform in Edge and Firefox.
*/
button,
select {
text-transform: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Remove default button styles.
*/
button,
[type='button'],
[type='reset'],
[type='submit'] {
-webkit-appearance: button;
/* 1 */
background-color: transparent;
/* 2 */
background-image: none;
/* 2 */
}
/*
Use the modern Firefox focus style for all focusable elements.
*/
:-moz-focusring {
outline: auto;
}
/*
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
*/
:-moz-ui-invalid {
box-shadow: none;
}
/*
Add the correct vertical alignment in Chrome and Firefox.
*/
progress {
vertical-align: baseline;
}
/*
Correct the cursor style of increment and decrement buttons in Safari.
*/
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
/*
1. Correct the odd appearance in Chrome and Safari.
2. Correct the outline style in Safari.
*/
[type='search'] {
-webkit-appearance: textfield;
/* 1 */
outline-offset: -2px;
/* 2 */
}
/*
Remove the inner padding in Chrome and Safari on macOS.
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button;
/* 1 */
font: inherit;
/* 2 */
}
/*
Add the correct display in Chrome and Safari.
*/
summary {
display: list-item;
}
/*
Removes the default spacing and border for appropriate elements.
*/
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
margin: 0;
}
fieldset {
margin: 0;
padding: 0;
}
legend {
padding: 0;
}
ol,
ul,
menu {
list-style: none;
margin: 0;
padding: 0;
}
/*
Prevent resizing textareas horizontally by default.
*/
textarea {
resize: vertical;
}
/*
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
2. Set the default placeholder color to the user's configured gray 400 color.
*/
input::-moz-placeholder, textarea::-moz-placeholder {
opacity: 1;
/* 1 */
color: #9ca3af;
/* 2 */
}
input::placeholder,
textarea::placeholder {
opacity: 1;
/* 1 */
color: #9ca3af;
/* 2 */
}
/*
Set the default cursor for buttons.
*/
button,
[role="button"] {
cursor: pointer;
}
/*
Make sure disabled buttons don't get the pointer cursor.
*/
:disabled {
cursor: default;
}
/*
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
This can trigger a poorly considered lint error in some tools but is included by design.
*/
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block;
/* 1 */
vertical-align: middle;
/* 2 */
}
/*
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
*/
img,
video {
max-width: 100%;
height: auto;
}
/* Make elements with the HTML hidden attribute stay hidden by default */
[hidden] {
display: none;
}
:root {
--summary-item-color-var: 0deg 100% 67%
}
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
}
.fixed {
position: fixed;
}
.inset-x-0 {
left: 0px;
right: 0px;
}
.bottom-1 {
bottom: 0.25rem;
}
.block {
display: block;
}
.flex {
display: flex;
}
.grid {
display: grid;
}
.h-12 {
height: 3rem;
}
.h-32 {
height: 8rem;
}
.h-full {
height: 100%;
}
.h-screen {
height: 100vh;
}
.w-32 {
width: 8rem;
}
.w-full {
width: 100%;
}
.w-screen {
width: 100vw;
}
.flex-1 {
flex: 1 1 0%;
}
.flex-row {
flex-direction: row;
}
.flex-col {
flex-direction: column;
}
.place-items-center {
place-items: center;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.gap-y-3 {
row-gap: 0.75rem;
}
.gap-y-4 {
row-gap: 1rem;
}
.rounded-full {
border-radius: 9999px;
}
.rounded-lg {
border-radius: 0.5rem;
}
.rounded-b-3xl {
border-bottom-right-radius: 1.5rem;
border-bottom-left-radius: 1.5rem;
}
.bg-dark-gray-blue {
--tw-bg-opacity: 1;
background-color: hsl(224 30% 27% / var(--tw-bg-opacity));
}
.bg-summary-item-color\/5 {
background-color: hsl(var(--summary-item-color-var) / 0.05);
}
.bg-gradient-to-t {
background-image: linear-gradient(to top, var(--tw-gradient-stops));
}
.from-light-royal-blue {
--tw-gradient-from: hsl(241, 81%, 54%) var(--tw-gradient-from-position);
--tw-gradient-to: hsl(241 81% 54% / 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.from-violet-blue\/60 {
--tw-gradient-from: hsla(256, 72%, 46%, 0.6) var(--tw-gradient-from-position);
--tw-gradient-to: hsla(256, 72%, 46%, 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.from-50\% {
--tw-gradient-from-position: 50%;
}
.to-light-slate-blue {
--tw-gradient-to: hsl(252, 100%, 67%) var(--tw-gradient-to-position);
}
.to-persian-blue\/75 {
--tw-gradient-to: hsla(241, 72%, 46%, 0.75) var(--tw-gradient-to-position);
}
.p-7 {
padding: 1.75rem;
}
.px-10 {
padding-left: 2.5rem;
padding-right: 2.5rem;
}
.px-5 {
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.pb-8 {
padding-bottom: 2rem;
}
.pr-2 {
padding-right: 0.5rem;
}
.pt-4 {
padding-top: 1rem;
}
.text-center {
text-align: center;
}
.text-5xl {
font-size: 3rem;
line-height: 1;
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.text-xl {
font-size: 1.25rem;
line-height: 1.75rem;
}
.font-bold {
font-weight: 700;
}
.font-extrabold {
font-weight: 800;
}
.text-dark-gray-blue {
--tw-text-opacity: 1;
color: hsl(224 30% 27% / var(--tw-text-opacity));
}
.text-pale-blue {
--tw-text-opacity: 1;
color: hsl(221 100% 96% / var(--tw-text-opacity));
}
.text-summary-item-color {
--tw-text-opacity: 1;
color: hsl(var(--summary-item-color-var) / var(--tw-text-opacity));
}
.text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
html {
font-size: 18px;
}
@font-face {
font-family: 'HankenGrotesk';
src: url('/static/public/fonts/static/HankenGrotesk-Medium.ttf') format('truetype');
font-weight: 400;
/* Regular */
font-style: normal;
}
@font-face {
font-family: 'HankenGrotesk';
src: url('/static/public/fonts/static/HankenGrotesk-Bold.ttf') format('truetype');
font-weight: 700;
/* Bold */
font-style: normal;
}
@font-face {
font-family: 'HankenGrotesk';
src: url('/static/public/fonts/static/HankenGrotesk-ExtraBold.ttf') format('truetype');
font-weight: 800;
/* ExtraBold */
font-style: normal;
}
@media (min-width: 768px) {
.md\:h-44 {
height: 11rem;
}
.md\:h-\[500px\] {
height: 500px;
}
.md\:w-1\/2 {
width: 50%;
}
.md\:w-44 {
width: 11rem;
}
.md\:w-\[715px\] {
width: 715px;
}
.md\:flex-row {
flex-direction: row;
}
.md\:justify-around {
justify-content: space-around;
}
.md\:rounded-3xl {
border-radius: 1.5rem;
}
.md\:bg-white {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
}
.md\:px-8 {
padding-left: 2rem;
padding-right: 2rem;
}
.md\:pt-2 {
padding-top: 0.5rem;
}
.md\:text-2xl {
font-size: 1.5rem;
line-height: 2rem;
}
.md\:text-6xl {
font-size: 3.75rem;
line-height: 1;
}
.md\:text-base {
font-size: 1rem;
line-height: 1.5rem;
}
.md\:text-xl {
font-size: 1.25rem;
line-height: 1.75rem;
}
.md\:shadow-2xl {
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.md\:shadow-light-lavender\/50 {
--tw-shadow-color: hsl(241 100% 89% / 0.5);
--tw-shadow: var(--tw-shadow-colored);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

1
18-expenses-chart/.envrc Normal file
View File

@@ -0,0 +1 @@
use flake

5
18-expenses-chart/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
/.direnv/
/.go/
*~
*_templ.go
static/output.css

View File

@@ -0,0 +1,28 @@
# List of all .templ files
TEMPL_FILES := $(shell find templates -type f -name '*.templ')
# Name of the generated file(s) from templ
GENERATED_FILES := $(TEMPL_FILES:.templ=_templ.go)
# Generate Go files from .templ files only if they have changed
$(GENERATED_FILES): $(TEMPL_FILES)
templ generate
TAILWIND_CONFIG := tailwind.config.js
INPUT_CSS := input.css
OUTPUT_CSS := static/output.css
# generate tailwindcss output
$(OUTPUT_CSS): $(TEMPL_FILES) $(TAILWIND_CONFIG) $(INPUT_CSS)
tailwindcss -i $(INPUT_CSS) -o $(OUTPUT_CSS)
# Run the server, with dependencies on templ and tailwind generation
run: $(GENERATED_FILES) $(OUTPUT_CSS)
go run .
# this uses wgo to re-execute 'make run'
# which will toggle templ generate, when needed
# and then do `go run .` to rebuild all else
.PHONY: run/live
run/live:
wgo -verbose -file=.go -file=.templ -file=$(INPUT_CSS) -file=$(TAILWIND_CONFIG) make run

View File

@@ -1,6 +1,6 @@
# Frontend Mentor - Results summary component solution
# Frontend Mentor - Expenses chart component solution
This is a solution to the [Results summary component challenge on Frontend Mentor](https://www.frontendmentor.io/challenges/results-summary-component-CE_K6s0maV). Frontend Mentor challenges help you improve your coding skills by building realistic projects.
This is a solution to the [Expenses chart component challenge on Frontend Mentor](https://www.frontendmentor.io/challenges/expenses-chart-component-e7yJBUdjwt). Frontend Mentor challenges help you improve your coding skills by building realistic projects.
## Table of contents
@@ -24,9 +24,11 @@ This is a solution to the [Results summary component challenge on Frontend Mento
Users should be able to:
- View the optimal layout for the interface depending on their device's screen size
- See hover and focus states for all interactive elements on the page
- **Bonus**: Use the local JSON data to dynamically populate the content
- View the bar chart and hover over the individual bars to see the correct amounts for each day
- See the current days bar highlighted in a different colour to the other bars
- View the optimal layout for the content depending on their devices screen size
- See hover states for all interactive elements on the page
- **Bonus**: Use the JSON data file provided to dynamically size the bars on the chart
### Screenshot
@@ -62,13 +64,6 @@ Then crop/optimize/edit your image however you like, add it to your project, and
### What I learned
#### restarting server of file watch
wgo -verbose -file .go -file .gohtml -file tailwind.config.js echo reloading :: bash -c 'tailwindcss -i ./input.css -o public/out.css' :: go run main.go
and doing tailwind first, because server embeds the output css file, so it's needed for the build step
#### sample
Use this section to recap over some of your major learnings while working through this project. Writing these out and providing code samples of areas you want to highlight is a great way to reinforce your own knowledge.
To see how you can add code snippets, see below:

View File

@@ -1,6 +1,6 @@
# Frontend Mentor - Results summary component
# Frontend Mentor - Expenses chart component
![Design preview for the Results summary component coding challenge](./design/desktop-preview.jpg)
![Design preview for the Expenses chart component coding challenge](./design/desktop-preview.jpg)
## Welcome! 👋
@@ -8,23 +8,25 @@ Thanks for checking out this front-end coding challenge.
[Frontend Mentor](https://www.frontendmentor.io) challenges help you improve your coding skills by building realistic projects.
**To do this challenge, you need a basic understanding of HTML and CSS.**
**To do this challenge, you need a decent understanding of HTML, CSS and JavaScript.**
## The challenge
Your challenge is to build out this results summary component and get it looking as close to the design as possible.
Your challenge is to build out this bar chart component and get it looking as close to the design as possible.
You can use any tools you like to help you complete the challenge. So if you've got something you'd like to practice, feel free to give it a go.
We provide the data for the results in a local `data.json` file. So you can use that to add the results and total score dynamically if you choose.
We provide the data for the chart in a local `data.json` file. So you can use that to dynamically add the bars if you choose.
Your users should be able to:
- View the optimal layout for the interface depending on their device's screen size
- See hover and focus states for all interactive elements on the page
- **Bonus**: Use the local JSON data to dynamically populate the content
- View the bar chart and hover over the individual bars to see the correct amounts for each day
- See the current day's bar highlighted in a different colour to the other bars
- View the optimal layout for the content depending on their device's screen size
- See hover states for all interactive elements on the page
- **Bonus**: See dynamically generated bars based on the data provided in the local JSON file
Want some support on the challenge? [Join our Slack community](https://www.frontendmentor.io/slack) and ask questions in the **#help** channel.
Want some support on the challenge? [Join our community](https://www.frontendmentor.io/community) and ask questions in the **#help** channel.
## Where to find everything
@@ -34,9 +36,7 @@ The designs are in JPG static format. Using JPGs will mean that you'll need to u
If you would like the design files (we provide Sketch & Figma versions) to inspect the design in more detail, you can [subscribe as a PRO member](https://www.frontendmentor.io/pro).
All the required assets for this project are in the `/assets` folder. The images are already exported for the correct screen size and optimized.
We also include variable and static font files for the required fonts for this project. You can choose to either link to Google Fonts or use the local font files to host the fonts yourself. Note that we've removed the static font files for the font weights that aren't needed for this project.
You will find all the required assets in the `/images` folder. The assets are already optimized.
There is also a `style-guide.md` file containing the information you'll need, such as color palette and fonts.
@@ -53,7 +53,7 @@ Feel free to use any workflow that you feel comfortable with. Below is a suggest
## Deploying your project
As mentioned above, there are many ways to host your project for free. Our recommend hosts are:
As mentioned above, there are many ways to host your project for free. Our recommended hosts are:
- [GitHub Pages](https://pages.github.com/)
- [Vercel](https://vercel.com/)
@@ -79,7 +79,7 @@ Remember, if you're looking for feedback on your solution, be sure to ask questi
There are multiple places you can share your solution:
1. Share your solution page in the **#finished-projects** channel of the [Slack community](https://www.frontendmentor.io/slack).
1. Share your solution page in the **#finished-projects** channel of the [community](https://www.frontendmentor.io/community).
2. Tweet [@frontendmentor](https://twitter.com/frontendmentor) and mention **@frontendmentor**, including the repo and live URLs in the tweet. We'd love to take a look at what you've built and help share it around.
3. Share your solution on other social channels like LinkedIn.
4. Blog about your experience building your project. Writing about your workflow, technical choices, and talking through your code is a brilliant way to reinforce what you've learned. Great platforms to write on are [dev.to](https://dev.to/), [Hashnode](https://hashnode.com/), and [CodeNewbie](https://community.codenewbie.org/).

View File

@@ -0,0 +1,30 @@
[
{
"day": "mon",
"amount": 17.45
},
{
"day": "tue",
"amount": 34.91
},
{
"day": "wed",
"amount": 52.36
},
{
"day": "thu",
"amount": 31.07
},
{
"day": "fri",
"amount": 23.39
},
{
"day": "sat",
"amount": 43.28
},
{
"day": "sun",
"amount": 25.48
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

211
18-expenses-chart/flake.lock generated Normal file
View File

@@ -0,0 +1,211 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1709126324,
"narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "d465f4819400de7c8d874d50b982301f28a84605",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_3": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"templ",
"nixpkgs"
]
},
"locked": {
"lastModified": 1694102001,
"narHash": "sha256-vky6VPK1n1od6vXbqzOXnekrQpTL4hbPAwUhT5J9c9E=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "9e21c80adf67ebcb077d75bd5e7d724d21eeafd6",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"gomod2nix": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": [
"templ",
"nixpkgs"
]
},
"locked": {
"lastModified": 1705314449,
"narHash": "sha256-yfQQ67dLejP0FLK76LKHbkzcQqNIrux6MFe32MMFGNQ=",
"owner": "nix-community",
"repo": "gomod2nix",
"rev": "30e3c3a9ec4ac8453282ca7f67fca9e1da12c3e6",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "gomod2nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1710097495,
"narHash": "sha256-B7Ea7q7hU7SE8wOPJ9oXEBjvB89yl2csaLjf5v/7jr8=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "d40e866b1f98698d454dad8f592fe7616ff705a4",
"type": "github"
},
"original": {
"owner": "nixos",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1701282334,
"narHash": "sha256-MxCVrXY6v4QmfTwIysjjaX0XUhqBbxTWWB4HXtDYsdk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "057f9aecfb71c4437d2b27d3323df7f93c010b7e",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"templ": "templ"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"templ": {
"inputs": {
"gitignore": "gitignore",
"gomod2nix": "gomod2nix",
"nixpkgs": "nixpkgs_2",
"xc": "xc"
},
"locked": {
"lastModified": 1709917943,
"narHash": "sha256-zDQxUFSmG/VX+xtK+nZ3ObRMVcMjjx+EUAxHLNcHHF8=",
"owner": "a-h",
"repo": "templ",
"rev": "df2a32403bb4a8e4745ac10ddc8b3e77386d8045",
"type": "github"
},
"original": {
"owner": "a-h",
"repo": "templ",
"type": "github"
}
},
"xc": {
"inputs": {
"flake-utils": "flake-utils_3",
"nixpkgs": [
"templ",
"nixpkgs"
]
},
"locked": {
"lastModified": 1703164129,
"narHash": "sha256-kCcCqqwvjN07H8FPG4tXsRVRcMqT8dUNt9pwW1kKAe8=",
"owner": "joerdav",
"repo": "xc",
"rev": "0655cccfcf036556aeaddfb8f45dc7e8dd1b3680",
"type": "github"
},
"original": {
"owner": "joerdav",
"repo": "xc",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -0,0 +1,80 @@
rec {
description = "expenses chart exercise";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs";
flake-utils.url = "github:numtide/flake-utils";
templ.url = "github:a-h/templ";
};
outputs = { self, nixpkgs, flake-utils, templ }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
templPkg = templ.packages.${system}.templ;
pname = "expenses-chart";
version = "0.0.1";
in rec {
thePackage = pkgs.buildGoModule {
inherit pname version;
src = pkgs.nix-gitignore.gitignoreSource [ ] ./.;
vendorHash = "sha256-sKuP3TsRD3MXBGtdSRnX62eXBQE1ASWFQu0kLlXSlFA=";
preBuild = ''
# Adding the Tailwind build step to preBuild
${pkgs.nodePackages.tailwindcss}/bin/tailwindcss -i ./input.css -o static/output.css
# Adding generation of go code from templ files
${templPkg}/bin/templ generate
'';
};
packages = rec {
"${pname}" = thePackage;
"${pname}-image" = pkgs.dockerTools.buildLayeredImage {
name = pname;
tag = "latest";
created = "now";
config = {
Cmd = [ "${thePackage}/bin/templ-exercise" ];
ExposedPorts = { "3000/tcp" = { }; };
};
};
image-hello = pkgs.dockerTools.buildLayeredImage { # so, wow, this works
name = pname;
tag = "latest";
config.Cmd = [ "${pkgs.hello}/bin/hello" ];
};
image = pkgs.dockerTools.buildImage {
name = pname;
tag = "latest";
created = "now";
copyToRoot = pkgs.buildEnv {
name = "image-root";
paths = [ thePackage pkgs.dockerTools.binSh pkgs.coreutils ];
pathsToLink = [ "/bin" "/dist" "/public" ];
};
config = {
Cmd = [ "/bin/templ-exercise" ];
ExposedPorts = { "8080/tcp" = { }; };
};
};
};
devShells.default = pkgs.mkShell {
buildInputs = [
pkgs.go
pkgs.wgo
pkgs.semgrep
pkgs.gopls
pkgs.gnumake
templPkg
pkgs.nodePackages.tailwindcss
];
shellHook = ''
export GOPATH=$PWD/.go
export PATH=$GOPATH/bin:$PATH
'';
};
});
# see https://serokell.io/blog/practical-nix-flakes
}

View File

5
18-expenses-chart/go.mod Normal file
View File

@@ -0,0 +1,5 @@
module sunshine.industries/temp-exercise
go 1.21.7
require github.com/a-h/templ v0.2.598

4
18-expenses-chart/go.sum Normal file
View File

@@ -0,0 +1,4 @@
github.com/a-h/templ v0.2.598 h1:6jMIHv6wQZvdPxTuv87erW4RqN/FPU0wk7ZHN5wVuuo=
github.com/a-h/templ v0.2.598/go.mod h1:SA7mtYwVEajbIXFRh3vKdYm/4FYyLQAtPH1+KxzGPA8=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@@ -0,0 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
html {
font-size: 18px;
}

33
18-expenses-chart/main.go Normal file
View File

@@ -0,0 +1,33 @@
package main
import (
"context"
"fmt"
"math/rand"
"net/http"
"sunshine.industries/temp-exercise/templates"
)
func randomDailyExpense() float32 {
return 10 + rand.Float32()*100
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
component := templates.IndexPage(templates.PageData{
Balance: 500 + rand.Float32()*1000,
Expenses: []float32{randomDailyExpense(), randomDailyExpense(), randomDailyExpense(), randomDailyExpense(), randomDailyExpense(), randomDailyExpense(), randomDailyExpense()},
TotalThisMonth: 50 + rand.Float32()*600,
PercentComparedToLastMonth: -5 + 10*rand.Float32(),
})
component.Render(context.Background(), w)
})
staticFs := http.FileServer(http.Dir("./static"))
http.Handle("/static/", http.StripPrefix("/static/", staticFs))
fmt.Println("starting to serve on :3000")
http.ListenAndServe(":3000", nil)
}

107
18-expenses-chart/notes.org Normal file
View File

@@ -0,0 +1,107 @@
* new exersice after long time
now will be learning https://github.com/a-h/templ
it's a templating solution for go, more similar to jsx
but it also needs a pre-processing step, so i'm adding it like tailwind
with Makefile for make run/live
and will need to add as a step in project build
* setting up
https://git.sunshine.industries/efim/golang-templ-and-tailwind
have separate repo, but i guess i'll remove .git from this dir,
to have single commits across Learning HTMX?
or maybe magit will just allow for simple integration?
just not pushing into origin, committing, and then committing from overall repository?
** not so simple, but still quite OK!
there's now 'subtree' which is separate concept from submodule,
and it is maybe simpler:
https://magit.vc/manual/magit/Subtree.html
i'm using the repo url
ssh://gitea@git.sunshine.industries:65433/efim/golang-templ-and-tailwind.git
and the command magit subtree add,
specify the directory, and it gets embedded it seems.
i have one level magit dispatch and my notes would be committed to the main repo,
i would potentially be able to pull in commits from the parent?
and maybe somehow contribute, but yeah, this is still confusing 🙂
how would i commit to subtree upstream if there are conflicting changes?
maybe i'll just get denied, as simple as that.
allright! let's add exercise specific data (again)
* so, starting dev
old process:
1. open second browser and open png with style
first mobile
2. enable view C-S-m in firefox
the responsive mode
i suppose next is adding colors to tailwind configuration?
from the styleguide file
also
3. check desktop style, whether any big rearrangements are required
so that i'd start mobile with way to rearrange things
do i have a separate note with all of these things?
i've also used some commands to vertically center my stuff
** DONE allright, tailwind config go
font, size, colors
** well, i don't really want to go on.
but yeah, i'll need hierarchy of the elements.
** ok. thoughs:
the componets would be
- my balance bubble
because it is spacially separate
- spending summary
- graph inside spending summary
or maybe don't even need that
the component should take up all space provided to it.
if parent would want to add space around - that's easy to do,
but if component insists on adding space around itself - harder to reuse?
because then if parent would want to have child take up all space - negative padding?
*** allright, @ to include sub templates
** well, last part? preparing the docker container for deployment on Render.com?
which port should be used?
also, this would be first time i'll add templ as a build step
** for some reason image doesn't work well
podman run -d -p 9090:8080 localhost/expenses-chart:latest /nix/store/l4r6glmzbvkhg97lp4dn7nm76w6hz41g-expenses-chart-0.0.1/bin/temp-exercise
this runs,
but podman run -it expenses-chart:latest
Error: crun: executable file `/nix/store/l4r6glmzbvkhg97lp4dn7nm76w6hz41g-expenses-chart-0.0.1/bin/templ-exercise` not found in $PATH: No such file or directory: OCI runtime attempted to invoke a command that was not found
oh, well
** you know what - i give up
for some reason when i open url to app running in container,
and it's on the 8080 port of the host machine - the static files are served
if i use any other port of local machine, to forward into container,
the static files are not served
maybe this is bug in go? i don't know
StripPrefix doesn't seem to do anything
and for some reason in this specific exercise CMD doesn't work in the container
so let's just give up!
i could try to write a docker file and check with that, but yeah

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<svg width="72" height="48" viewBox="0 0 72 48" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle fill="#382314" cx="48" cy="24" r="24"/><circle stroke="#FFF" stroke-width="2" cx="24" cy="24" r="23"/></g></svg>

After

Width:  |  Height:  |  Size: 238 B

View File

@@ -0,0 +1,37 @@
# Front-end Style Guide
## Layout
The designs were created to the following widths:
- Mobile: 375px
- Desktop: 1440px
> 💡 These are just the design sizes. Ensure content is responsive and meets WCAG requirements by testing the full range of screen sizes from 320px to large screens.
## Colors
### Primary
- Soft red: hsl(10, 79%, 65%)
- Cyan: hsl(186, 34%, 60%)
### Neutral
- Dark brown: hsl(25, 47%, 15%)
- Medium brown: hsl(28, 10%, 53%)
- Cream: hsl(27, 66%, 92%)
- Very pale orange: hsl(33, 100%, 98%)
## Typography
### Body Copy
- Font size: 18px
### Font
- Family: [DM Sans](https://fonts.google.com/specimen/DM+Sans)
- Weights: 400, 700
> 💎 [Upgrade to Pro](https://www.frontendmentor.io/pro?ref=style-guide) for design file access to see all design details and get hands-on experience using a professional workflow with tools like Figma.

View File

@@ -0,0 +1,29 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./templates/index.templ"],
theme: {
extend: {
fontFamily: {
'sans': ['DM Sans', 'sans-serif'],
},
fontWeight: {
'normal': 400,
'bold': 700,
}
},
colors: {
primary: {
'soft-red': 'hsl(10, 79%, 65%)',
cyan: 'hsl(186, 34%, 60%)',
},
neutral: {
'dark-brown': 'hsl(25, 47%, 15%)',
'medium-brown': 'hsl(28, 10%, 53%)',
cream: 'hsl(27, 66%, 92%)',
'very-pale-orange': 'hsl(33, 100%, 98%)',
},
}
},
plugins: [],
}

View File

@@ -0,0 +1,133 @@
package templates
import "fmt"
import "slices"
type PageData struct {
Balance float32
Expenses []float32
TotalThisMonth float32
PercentComparedToLastMonth float32
}
templ myBalanceComponent(balance float32) {
<div class="flex flex-row rounded-xl text-neutral-very-pale-orange text-xs md:text-base bg-primary-soft-red p-4 shadow md:rounded-[1rem] md:p-7">
<div class="grow ">
My balance
<p class="text-xl md:text-2xl font-bold">${ fmt.Sprintf("%.2f", balance) }</p>
</div>
<img
src="./static/images/logo.svg"
alt="App logo: a white circle overlapping from the left a filled in black circle"
/>
</div>
}
var days []string = []string{"mon", "tue", "wed", "thu", "fri", "sat", "sun"}
// returns templ SummaryComponent
// with all attributes computed from the expenses slice
// the percentages of the columns, current day number, etc
// NOTE: this seems to be the way to mix go calculations and templates
func prepareSummaryComponent(expenses []float32, totalThisMonth, percentComparedToLastMonth float32) templ.Component {
fmt.Println("hello, preparing expenses: ", expenses)
max := slices.Max(expenses)
percentages := make([]float32, 0, len(expenses))
for _, price := range expenses {
percentages = append(percentages, price/max)
}
currentDayNum := 2
return spendingSummaryComponent(expenses, percentages, currentDayNum, percentComparedToLastMonth, totalThisMonth)
}
css expenseBarVars(percentage float32) {
--height-percentage: { fmt.Sprintf("%.2f", percentage) };
height: calc(var(--height-percentage) * 8rem);
}
templ dayExpenseColumn(expense, percentage float32, day string, isCurrentDay bool) {
<div class="flex flex-col justify-end items-center h-32">
<div
class={ "rounded group relative w-full h-12",
expenseBarVars(percentage),
templ.KV("bg-primary-soft-red hover:bg-primary-soft-red/70", !isCurrentDay),
templ.KV("bg-primary-cyan hover:bg-primary-cyan/70", isCurrentDay),
"hover:cursor-pointer" }
>
<span class="absolute left-1/2 -translate-x-1/2 -translate-y-7 opacity-0 group-hover:opacity-100 bg-neutral-dark-brown text-neutral-cream rounded text-xs p-1">${ fmt.Sprintf("%.2f", expense ) }</span>
</div>
<p class="text-neutral-medium-brown text-xs py-2">{ day }</p>
</div>
}
// The 7 vertical bars of the per-day expenses
templ expensesChart(expenses, percentages []float32, currentDayNum int) {
<div class="grid grid-cols-7 place-content-between gap-x-3">
for i := 0; i < 7; i++ {
@dayExpenseColumn(expenses[i], percentages[i], days[i], currentDayNum == i)
}
</div>
}
// Big container with chard and total expenses of the week
templ spendingSummaryComponent(expenses, percentages []float32, currentDayNum int, totalThisMonth, percentComparedToLastMonth float32) {
<div class="bg-neutral-very-pale-orange rounded-xl shadow p-4 py-6 flex flex-col gap-y-4 md:p-7 md:px-10 md:rounded-[1rem] md:gap-y-5">
<p2 class="text-neutral-dark-brown text-xl font-bold pb-6 md:text-2xl md:pb-12 ">Spending - Last 7 days</p2>
@expensesChart(expenses, percentages, currentDayNum)
<hr class="bg-neutral-cream border-t-0 h-0.5"/>
<div class="grid grid-cols-2 text-sm text-neutral-medium-brown">
<p class="col-span-full">Total this month</p>
<div class="grow">
<p class="grid items-center text-neutral-dark-brown text-2xl font-bold h-full md:text-4xl md:py-2">$ { fmt.Sprintf("%.2f", totalThisMonth) }</p>
</div>
<div class="text-right grid content-center">
<div class="text-neutral-dark-brown font-bold ">
if (percentComparedToLastMonth > 0) {
<span class="inline">+</span>
}
{ fmt.Sprintf("%.2f", percentComparedToLastMonth) }%
</div>
from last month
</div>
</div>
</div>
}
templ WidgetsComponend(pageData PageData) {
<div class="flex flex-col gap-4">
@myBalanceComponent(pageData.Balance)
@prepareSummaryComponent(pageData.Expenses, pageData.PercentComparedToLastMonth, pageData.TotalThisMonth)
</div>
}
templ IndexPage(pageData PageData) {
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap" rel="stylesheet"/>
<link rel="stylesheet" href="/styles/templ.css"/>
<link href="/static/output.css" rel="stylesheet"/>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <!-- displays site properly based on user's device -->
<link rel="icon" type="image/png" sizes="32x32" href="./static/images/favicon-32x32.png"/>
<title>Frontend Mentor | Expenses chart component</title>
<!-- Feel free to remove these styles or customise in your own stylesheet 👍 -->
<style>
.attribution { font-size: 11px; text-align: center; }
.attribution a { color: hsl(228, 45%, 44%); }
</style>
</head>
<body class="bg-neutral-cream text-bold p-4 h-full grid place-items-center">
<main class="w-full md:w-1/4">
@WidgetsComponend(pageData)
</main>
<div class="attribution fixed bottom-0 inset-x-0">
Challenge by <a href="https://www.frontendmentor.io?ref=challenge" target="_blank">Frontend Mentor</a>.
Coded by <a href="#">Your Name Here</a>.
</div>
</body>
</html>
}