with tasks filter: https://www.frontendmentor.io/challenges?sort=difficulty|asc&difficulties=2&types=free-plus,free&hideCompleted=true
site with projects to practice front end dev, let’s try this out
file:///home/enefedov/Documents/personal/frontendmentor/ link to open designs in firefox
https://laminar.dev/documentation#parsing-html-or-svg-strings-into-elements
div {
outline: 1px solid red;
}
https://www.frontendmentor.io/challenges/product-preview-card-component-GO7UmttRfa/hub
enefedov@enefedov-ubuntu:~$ nix run nixpkgs#gpick
https://www.frontendmentor.io/challenges/interactive-rating-component-koxpeBUmI https://www.frontendmentor.io/challenges/interactive-rating-component-koxpeBUmI/hub
def renderAttribution(): Element = {
footerTag(
role := "contentinfo",
className := "absolute inset-x-0 bottom-2 attribution",
"Challenge by ",
a(
href := "https://www.frontendmentor.io?ref=challenge",
target := "_blank",
"Frontend Mentor"
),
" Coded by ",
a(href := "#", "Your Name Here")
)
}
https://firefox-source-docs.mozilla.org/devtools-user/measure_a_portion_of_the_page/index.html
DevTools > Settings > Available tools > Meausre part of page
then ruler appears in the dev tools, yay
html {
font-size: 14px;
}
https://www.rapidtables.com/convert/color/hsl-to-rgb.html enefedov@enefedov-ubuntu:~$ nix run nixpkgs#gpick
https://dequeuniversity.com/rules/axe/4.6/landmark-one-main?application=axeAPI https://dequeuniversity.com/rules/axe/4.6/page-has-heading-one?application=axeAPI <header role=”banner”> <nav role=”navigation”> <main role=”main”> <footer role=”contentinfo”>
https://dequeuniversity.com/rules/axe/4.6/page-has-heading-one?application=axeAPI use at least one <h1> to mark beginning of content
and put rustywind binary into /usr/bin
(defun my/rustywind-in-project-root ()
(interactive)
(let ((command (mapconcat 'identity
(list "rustywind ."
"--custom-regex"
(shell-quote-argument "className := \"([^\"]+)\"")
"--write") " ")))
(projectile-run-shell-command-in-root command)
(revert-buffer t t)))
(add-hook 'after-save-hook #'my/rustywind-in-project-root)
(remove-hook 'after-save-hook #'my/rustywind-in-project-root)
https://www.frontendmentor.io/challenges/age-calculator-app-dF9DFFpj-Q https://www.frontendmentor.io/challenges/age-calculator-app-dF9DFFpj-Q/hub
starting with it. copying the initted directory, changing names, and unpacking resources.
product-preview-card-component
they all should share the Date object, i suppose three inputs would be in a single component? they would have their own validation, maybe defined in the top component, where there’s Date and what? how do i connect them to the single model? there’s a way surely
https://github.com/scala-js/scala-js/security/advisories/GHSA-j2f9-w8wh-9ww4 Patches
Scala.js v1.10.0 fixes the issue. It uses java.security.SecureRandom to implement randomUUID().
java.security.SecureRandom is not provided by Scala.js core. Therefore, to be able to use randomUUID(), you will need to add a dependency on scalajs-java-securerandom. Failing to do so will result in linking errors (i.e., fastLinkJS/fullLinkJS will fail).
ok, so, i also need to reset date to None when there’s error, yuk
libraryDependencies += (“org.scala-js” %%% “scalajs-java-securerandom” % “1.0.0”).cross(CrossVersion.for3Use2_13)
There’s no built-in way in Firefox (or any browser) to adjust the positioning of an image opened directly in a browser tab. However, you can use a simple JavaScript hack in the browser console to accomplish this:
Open your browser’s console by pressing F12 or Ctrl+Shift+K on your keyboard.
In the console, paste the following command and hit Enter:
document.querySelector('img').style.marginTop = "50px";
Replace “50px” with whatever amount of spacing you’d like. This command will add a top margin to the image, pushing it down the page. However, please note that this change is temporary and will not persist when the page is refreshed. If you want a permanent solution, you would need to use CSS in the webpage’s code to adjust the image’s positioning.
i can bind styles of others to it, but it doesn’t have “.amend”, so i could put “validatedElement.el” into the dom, and call “.amend” on it
https://www.frontendmentor.io/challenges/newsletter-signup-form-with-success-message-3FC1AZbNrv/hub yay
in real world can be filled after receiving 200 from backend.
https://www.frontendmentor.io/challenges/interactive-comments-section-iG1RugEG9/hub
with text field and button to submit Reply
so, walk all? or, top level are comments and others are replies? ok, yeah, there are two different models. ok, ok. let’s change that.
should have a component for the comment or reply.
maybe get the subcomponent that displays either, by the shared trait. and almost everything should be same?
i guess on the backend side it could be Cassandra with messages, and what? stored in same table, with optional fields
the Reply function would be different for Commend and Reply. or, not too different, but yeah
but that function can be initialized by prent container
the Reply window active state, can be hidden in the same Message Component, and opened when activating Reply.
it would be able to take in different action based on parent, whether it’s CommentReply or ReplyReply,
but lots of common logic:
the position of Reply and Vote elements are totally different on desktop and mobile for the first time attempting to do with Grid, potentially overcomplicating?
to track what person already voted? i could disallow re-voting for now, or just store 2 lists of IDs, who upvoted and who downvoted and allow re-voting
as separate component, ok
https://blog.webdevsimp lified.com/2023-04/html-dialog/ let’s try to do the dialog
yeah. let’s just do a bit somewhat copy? it would also always be for own
done in ugly and partial way. inlining svg would allow it to be styled with css?
with "text-blue-500 fill-current"
?
you know what! I will not be doing this! the design also wants me to bug ‘at-tag’ into response text field, and what am i to do with them? are they editable? this is not what I want
let’s do it with upickle and json
i guess yeah, passing in function on what to submit and where, and it could be reused.
maybe even go to the place in the page for the thing that got replied to it can be both Comment and Reply
and what? are they supposed to be editable?
need dependency https://github.com/cquiroz/scala-java-time libr
aryDependencies += "io.github.cquiroz" %%% "scala-java-time" % "2.5.0",
[error] – Error: /home/enefedov/Documents/personal/frontendmentor/6-interactive-comment-section/src/main/scala/industries/sunshine/interactivecommentsection/CommentComponent.scala:25:35 [error] 25 | stateVar.zoom(.comments.eachWhere(.message.id == uid))((state, newMessage) => { [error] | ^^^^^^^^^ [error] |Reference to method eachWhere in package com.softwaremill.quicklens should not have survived, [error] |it should have been processed and eliminated during expansion of an enclosing macro or term erasure.
this was because zoom is probably also a macros, and also should just have a getter as first argument
also, would be better to just split into mapped signal and function that does lense update in the parent
val deletionDialog = dialogTag(
className := "backdrop:bg-black/50",
form(
method := "dialog",
p("Are you sure?"),
button(
`type` := "submit", // html logic, will close modal, and not submit form
onClick --> Observer(_ => onDelete()),
onClick --> Observer(_ => println("submittign form")),
"YES, DELETE"
),
)
)
div(
className := "flex flex-row items-center",
deletionDialog, // put dialog into dom
button(
className := "flex flex-row items-center mr-7 text-sm font-bold text-soft-red",
onClick --> Observer(_ => deletionDialog.ref.showModal()), // acces dom ref, it's already typed. wow
"Delete"
),
)
className := "backdrop:bg-light-gray/75",
https://tailwindcss.com/docs/background-color#changing-the-opacity
https://www.scala-js.org/api/scalajs-dom/0.6/index.html#org.scalajs.dom.WindowLocalStorage
onMountInsert onMountCallback
also ctx.thisNode.ref.focus() access
also html “autofocus” attribute, which is not what i want, or, yeah. exactly what i want! no, not what I want - when the page loads.
visibility <-- messageSignal.map(msg => if (msg.user != selfUser) "visible" else "collapse"),
but actually in design, you should be able to vote for yourself
https://github.com/github/relative-time-element
npm install @github/relative-time-element
<relative-time datetime="2014-04-01T16:30:00-08:00">
April 1, 2014
</relative-time>
just name and datetime
attribute
val relativeTimeTag = htmlTag("relative-time")
val relTimeDatetime = htmlAttr("datetime", StringAsIsCodec)
and then used it:
div(
className := "pl-3 text-light-gray",
child <-- messageSignal.map(message => {
val scalaInstant = message.createdAt
val isoTime =
java.time.Instant.ofEpochSecond(scalaInstant.getEpochSecond())
relativeTimeTag(
scalaInstant.toString().take(10),
relTimeDatetime := isoTime.toString()
)
})
<!– TODO i have no idea why code doesn’t work without it – – in the console i see ‘already-registered’ –> <script type=”module”> import RelativeTimeElement from ‘/node_modules/@github/relative-time-element/dist/index.js’; if (!window.customElements.get(‘relative-time’)) { window.customElements.define(‘relative-time’, RelativeTimeElement); } else { console.log(“already-registered”) } </script>
Now, this was an increase in complexity!
Few things are undone:
- Not fully understanding reasons for needing “registration” a relative-time webcomponent in my `index.html` - specifically because log shows “already registered”, so code does nothing? But without it things don’t work.
- Changing color of SVG elements, maybe it’s easy if I inline them as code
- Not adding ‘@’ tag to replies. current component hierarcy makes it awkward to conditionally display ‘@’ in replies and not in top level comments, also - in the “edit” mode, design docs are not detailed enough to explain presence of ‘@user’ - is it editable? what happens when it’s edited?
Note: code would be a lot simpler in a full-stack app. With laminar I can propagate Stream of app state to all components, from the server websocket that sends all updates, so no callbacks to register state chages from the child components would be necessary, only doing http requests to api from the child components.
Things done:
- Message component
- display message, author, score
- functionality for reply
- checking and displaying if OWN
- functionality to delete - create modal https://blog.webdevsimplified.com/2023-04/html-dialog/
- functionality to edit - swap body with form
- make hover on buttons - making them more white
- save state into local storage with easy serialization into JSON
- component to reply \ send message could be shared?
- component for Whole Comment with Replies
- component for all comments
- component for whole comment section & submit new top level ui
- text wrap, when word is longer than element width
- submit by ctrl + Enter
- let’s style the desktop version
- add relative time webcomponent
article about Frontendmentor to reference sometime : https://medium.com/frontend-mentor/how-to-get-the-most-out-of-frontend-mentor-bdd6fdc25cb8
https://www.frontendmentor.io/challenges/news-homepage-H6SWTa1MFl/hub and I’m downgrading to Junior i guess this is more about html and css
with title, description, and image link. and in the New use that same, model, just don’t reference image
there’s almost zero coding logic, but layout needs to be thought out.
still ok to implement as separate div component with links, and then wrap in modal
on moble with menu button, on desktop directly with links. i guess could have 2 components, and then wrapped into logic of showing one or another
from 3 columns, goes to 1 column on mobile. i could have named areas, but i guess just direct order is good enough? big elements seem to always be 3 rowh in height and small stories can just be directly in the grid with 1fr height and 1fr width
maybe 170 per row and 60 gap?
https://efim-frontendmentor-news-homepage.pages.dev/
@font-face {
font-family: 'Inter';
src: url('/assets/fonts/static/Inter-Regular.ttf') format('truetype');
font-weight: 400; /* Regular */
font-style: normal;
}
@font-face {
font-family: 'Inter';
src: url('/assets/fonts/static/Inter-Bold.ttf') format('truetype');
font-weight: 700; /* Bold */
font-style: normal;
}
@font-face {
font-family: 'Inter';
src: url('/assets/fonts/static/Inter-ExtraBold.ttf') format('truetype');
font-weight: 800; /* ExtraBold */
font-style: normal;
}
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.scala",
],
theme: {
extend: {
fontFamily: {
'inter': ['Inter', 'sans-serif'],
},
},
},
plugins: [],
}
div(
className := "font-inter",
" Home "
)
with modal mobile menu, lot’s of help from chat gpt, so there are lots of code? articles about laminar?
or maybe it reads articles about doing this with js and translates this logic into laminar?
current way.
https://www.frontendmentor.io/solutions/solution-using-html-and-css-with-flex-and-grid-HaxK3b5tEb
Hi! I’m not a senior in frontend dev, but wanted to try to figure out your question: “In the mobile view, the text has moved together a bit, what could be the reason or how can this be fixed?”
What I did is opened your website in Firefox, entered mobile view with “Ctrl + Shift + M” (or Menu -> More Tools -> Mobile Design Mode) and then opened dev tools with F12.
The “context menu” (right click) on the text and then “inspect element” highlight that element in the dom in the menu, and also allow us to see sized of “body \ padding \ margin”
I’m putting the screenshots in my repo and will paste links:
- this is me inspecting the element in question: https://github.com/efim/frontendmentor-exercises/blob/master/feedback/1/looking-for-padding-reason-2023-06-06_18-59.png you can see that putting cursor at the blue inner rectangle (1) - the body gets highlighted at the page (2), but no padding or margin is detected
- then I check elements higher in the hierarchy, and finding this one https://github.com/efim/frontendmentor-exercises/blob/master/feedback/1/found-padding-2023-06-06_18-58.png setting cursor to (1) purple “padding” place - highlights the area of your webpage that padding takes (2) so, this is the element that results in text getting smushed
- then i can also temporarily change the style right in the browser dev tools: https://github.com/efim/frontendmentor-exercises/blob/master/feedback/1/changing-manually-2023-06-06_19-00.png setting left and right to 1rem removes this smushing
You’ll probably want to have media queries for mobile size that set different paddings for the element. and maybe even not using paddings to make blue background take whole width, but just make it’s width 100% or something. Here unfortunately I can’t give specific advice, because I’ve started almost immediately with TailwindCSS and it’s a bit different from css
Hope this helps!
https://sap.github.io/ui5-webcomponents/playground/components and their bindings for laminar: https://index.scala-lang.org/sherpal/laminarsapui5bindings examples : https://sherpal.github.io/laminar-ui5-demo/?componentName=
https://github.com/uosis/laminar-web-components https://laminar.dev/examples/web-components
was recommended to look into “Dragula” or “SortableJS” libraries, which should be somehow used from ScalaJS
there’s also Drag and Drop api in HTML 5, but it seems low level, the elements could emit “dragstart”, or “dragover”, or “dragend” events, but i’d have to implement handlers for updating indices in the list (easy part), and showing element while it’s dragged (should i? hard part)
https://www.scala-js.org/doc/tutorial/scalablytyped.html
https://www.frontendmentor.io/challenges/todo-app-Su1_KokOW
but let’s skip this part for now
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
http://cquiroz.github.io/scala-java-time/
libraryDependencies += “io.github.cquiroz” %%% “scala-java-time-tzdb” % “2.0.0-RC3_2019a”
ability to set the target date. and also in that menu let’s show also updating millis, just to get proof that thing is fast at this
do i want to allow to change “local” time zone?
https://www.svgrepo.com/svg/509956/gear?edit=true
it’s opening, but for some reason not closing. dialog and modal in Laminar ScalaJS why would that be?
Well, the TailwindCSS styling took me much longer than I anticipated. I was expecting a very quick task, instead the background was not trivial, the round cutouts in the clock element - much more complicated that i thought, not to say anything about the animation.
I was successful though in displaying countdown, and very happy with the speed of changing display - with milliseconds in the optional settings menu.
Have a few things to try out in ScalaJS, and then potentially get a pause in use of that nice technology, which still feels a big clunky when I try to reuse existing JS libraries, whithout which there’s too much to be created anew.
will do first map?
https://geo.ipify.org/api/v2/country,city?apiKey=YOUR_API_KEY&ipAddress=8.8.8.8 GET
- ipAddress
- Optional. IPv4 or IPv6 to search location by. If the parameter is not specified, then it defaults to client request’s public IP address.
- domain
- Optional. Domain name to search location by. If the parameter is not specified, then ‘ipAddress’ will be used.
{
"ip": "8.8.8.8",
"location": {
"country": "US",
"region": "California",
"city": "Mountain View",
"lat": 37.40599,
"lng": -122.078514,
"postalCode": "94043",
"timezone": "-07:00",
"geonameId": 5375481
},
"domains": [
"0d2.net",
"003725.com",
"0f6.b0094c.cn",
"007515.com",
"0guhi.jocose.cn"
],
"as": {
"asn": 15169,
"name": "Google LLC",
"route": "8.8.8.0/24",
"domain": "https://about.google/intl/en/",
"type": "Content"
},
"isp": "Google LLC"
}
ok, i guess.
input would be populated with own ip.
and then what? i guess app can have a state with case class of all info. and when it’s None - show loader. and start some kind of action when first visiting, tied to setter of state. and same logic could be used on a button.
and i guess i’d want the input would be form, and ip address validation. okok.
could write a funtion that takes hardcode and puts into state.
do i create a component with position ‘fixed’ and set it up from the parent component? so that what? function to set map coords could be exposed?
and map div, let’s keep it green of specific size get stream of coords. and what would it show on empty coords? whatever. would be nice to have animation
so, is that just single component? seems simple enought to be one file.
gets setter for the state
connected to state, which is on top level gets stream of state.
well, i’ll want to tailor class to the sample json? would that be easy to do with upickle? let’s just use deriving and cover full json?
yeah. ok, not exactly, but let’s go on with this.
i suppose at the level that defines state.
<input
type="text"
required <!-- don't allow empty field -->
pattern="^((\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$"
<!-- check value against regex on submission -->
title := "IPv4 address, like 192.168.0.1"
<!-- this is added to validation error message after 'Please match the expected format:' -->
>
</input>
this is already really cool!
can't do validation against the database, or from a combination of fields,
but seems good enough for many use cases. also there are many custom types, like email and whatnot.
with laminar small complication: html5 checks are done on default behavior, so if i’m disabling it (to prevent page reload), i need to call the logic on the elements:
form(
className := "flex flex-row",
input(
className := "px-5 rounded-l-xl grow",
placeholder <-- Utils.isMobileWidthStream.map(
if (_) placeholderTextSmall else placeholderText
),
typ := "text",
required := true,
pattern := """^((\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$""",
title := "IPv4 address, like 192.168.0.1",
controlled(
value <-- ipInput,
onInput.mapToValue --> ipInput
)
),
onMountInsert(ctx =>
button(
className := "grid place-content-center w-12 h-12 bg-black rounded-r-xl",
typ := "submit",
img(
src := "/images/icon-arrow.svg",
alt := "Start search"
),
onClick.preventDefault.map(_ =>
val validity = ctx.thisNode.ref.checkValidity()
if (!validity) ctx.thisNode.ref.reportValidity()
validity
) --> Observer(isFormValid => {
println(s"submitted ${ipInput.now()} for form instate $isFormValid")
})
)
)
)
So, “submitButton” is being created with access to form’s context, to allow easy usage of form’s ref and it’s functions:
checkValidity
:: boolean value if fields have passed validationreportValidity
:: funciton that triggers invalid state and messages in invalid fields
I’m not sure programmatic validation of fields is easily integrated with this html5, if i only want it for some fields. for posteriority, here’s link to one way of validating forms in Laminar, with logic to change field states - https://laminar.dev/examples/form-state
this is giude for setting up ScalablyTyped with externally managed npm: https://www.scala-js.org/doc/tutorial/scalablytyped.html
searching for verisons at https://www.npmjs.com/ ?
npm install leaflet npm insall -D @types/leaflet typescript
here i got news about fresher version: https://scalablytyped.org/docs/plugin
https://leafletjs.com/examples/quick-start/
how can i set css styles “after” loading of the library? if i’m bundling code? whelp, let’s add to index.html
first hardcoding return, shared between initial setup, and form submittion and i could still keep the hardcoded initail star
https://laminext.dev/v/0.15.x/t
https://seeip.org/ but it’s backend only, because of CORS
https://ip-api.com/docs/api:json
but! for Tbilisi it didn’t have lat lng:
got FetchResponse(true,200,OK,[object Headers],cors,{“ip”:”176.221.162.56”,”location”:{“country”:”GE”,”region”:”K’alak’i T’bilisi”,”timezone”:”+04:00”},”as”:{“asn”:35805,”name”:”SILKNET-AS”,”route”:”176.221.128.0\/17”,”domain”:”http:\/\/www.silknet.com”,”type”:”Cable\/DSL\/ISP”},”isp”:”JSC "Silknet"”},https://geo.ipify.org/api/v2/country?apiKey=at_kxjQ1RWEj6dIsnWJwpOxHzuVlrPyT)
just put it on the left side of modifiers of ‘main’ cool.
https://efim-frontendmentor-ip-tracker.pages.dev/
Yes, I’ve not completed things fully:
- want to try free api - https://ip-api.com/docs/api:json it shouldn’t have “500 requests” limit, and my sample site would be available for longer
- use grid in the info panel
- write good readme
And add cool notes to this submittion. But! It works and I really want to cross it off my mind for a moment. ScalaJS, Laminar and integration with Leaflet maps through automatically binding that were automatically generated from Typescript interface by ScalablyTyped project, yay!
https://github.com/Thewatcher13/product-card-perfume/tree/main
i found out about
https://www.frontendmentor.io/challenges/todo-app-Su1_KokOW/hub
i’ve kind of successfully set up sorting in example project. my guess is that facade is not generated correctly.
maybe would be cool to wrap unavailable things on my own. but OKOKOK, this is not quite good, but “good enough”.
is good enough actually a good thing here? i suppose that in actual production website it could be good enough, if ScalaJS had advantages in other areas.
i guess could be done as as in previous, with negative z level, here’s no map for interaction.
and icon on the change theme button and also tailwind classes, but i’ll read about that later
- list
- text
- isComplete
and counter of how many are left and “filter” buttons
so, what we put a filter into state i guess?
ok, how should i use filter. at the list level? it could be, yes. State at list level, with storing function in case classes yeah, and apply that to the incoming signal of list of element. yeah. ok.
and also signal of current state, and function to update state with own filter, and check of is active seems easy then, yay
with complete and delete buttons
https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually
by default follows system theme, can be done programmatically via js setting class “dark” to some parent tag or have both using “matchMedia” api: https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
and repo : https://github.com/efim/frontendmentor-exercises/tree/master/10-todo-list
// className := "group-hover:bg-red-500 md:group-hover:visible",
// className := "md:hidden group-hover:bg-red-500 md:group-hover:visible",
// className := "visible hover:invisible", // so this works
className := "md:invisible md:group-hover:visible",
let’s just let it go for now.
https://blog.webdevsimplified.com/2023-05/responsive-images/
picture tag would allow to have different sources
this seems maybe interesting. oh, for when i’m designing a gallery for example!
wow, thank you web dev simplified: https://blog.webdevsimplified.com/2023-05/responsive-images/ and it turns out there’s big article from Mozilla: https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images cool.
difference between <src> size set - it doesn’t downgrade image, it’s purpose is bandwidth optimization <picture> would change images in screen size increase and decrease - it’s purpose is selecting image for the media query size
very cool.
https://tailwindcss.com/docs/pointer-events
exactly for putting icons on top of inputs, right?
map and sorting. cool.