TL;DR
Beware of the crypto scams. Here I’m just sharing one that I just got from a site I never even heard of. Still not clear what could they do with my IBAN – assuming it’s just the first step in them trying to extract more info…
Anyways, stay careful 🤗
!TL;DR
The Email
Just so that this hopefully gets picked up by Google and people see it before it hits their email, I’m sharing the text of the email in full:
Dobar dan, I sam Monika Verbic, i predstavljam tvrtku Firebocks. Želim vas obavijestiti da imate neaktivan račun u našem sustavu koji je automatski otvoren 2019. godine.
https://blockchair.com/bitcoin/address/bc1q4cyj5ucd9xenehxyjjm24ycv84c44c4eyf50r8
Na taj račun je uplaćen bonus u iznosu od 250 eura koji ste dobili prilikom registracije. Nakon toga ste aktivirali sesiju automatskog trgovanja, a naš robot je obavljao trgovinske operacije na vašem računu tijekom 5 godina. Zahvaljujući rastu vrijednosti kriptovaluta u tom razdoblju, saldo vašeg računa trenutno iznosi 4670 eura.
Sesija trgovanja je završena, a prema pravilima naše tvrtke, registraciju je potrebno zatvoriti. Međutim, zatvaranje nije moguće dok na računu postoji pozitivan saldo. Zbog toga je potrebno prebaciti sredstva s ovog računa na vaš bankovni račun kako bi se registracija mogla uspješno zaključiti i izbjegli dodatni troškovi provizije.
Molimo vas da nam dostavite IBAN legalnog hrvatskog računa na koji želite primiti sredstva kako bismo mogli izvršiti prijenos. Ako imate bilo kakvih pitanja ili trebate dodatne informacije, slobodno mi se obratite u bilo kojem trenutku.
Hvala vam na suradnji!
—
Monika Verbic
Manager Fireblocks
https://www.fireblocks.com/
The crazy part? Someone who claimed to be Monika called me just before this email was sent (I hung up the phone as soon as the person started saying "you have a trading account with us…").
As crypto is becommoing more lucrative and even a household name, it’s not surprising that the scams are picking up in volume and also becoming increasingly sophisticated. Malicious actors continually find new ways to deceive unsuspecting victims. Recently, I received an email that’s a textbook example of a scam, and I want to share this experience to help you avoid falling for similar traps.
Too Good to Be True
The email, which claimed to be from a company called "Fireblocks," stated that I had an inactive account with a balance of 4,670 euros, earned through a supposed cryptocurrency trading bot. The message seemed professional and convincing at first glance, complete with links to a Bitcoin wallet and a seemingly legitimate website.
The twist? To "retrieve" the funds, they requested my IBAN (bank account information). This is a classic phishing technique—luring you in with promises of a large payout while stealing sensitive financial details.
Red Flags in the Email
- I never signed up for such a service or company
- Legitimate companies won’t ask for sensitive details like your IBAN over email.
- Promises of free money are almost always scams
- While the email included a link to a Bitcoin address and the company website, these are likely designed to create a false sense of legitimacy
- Use of the letter "I" instead of "ja" in Croatian language
- Misspelled name of the actual company
- Use of a private Gmail account, instead of one coming from a company domain
How to Protect Yourself
- Verify the Sender: Check the email address carefully. In this case, the address was from a generic domain, not an official company domain
- Don’t Click on Links: Hover over links to see their true destination before clicking. Avoid clicking links in unsolicited emails altogether
- Contact the Company Directly: If you suspect the email might be legitimate, visit the company’s official website and contact them through their verified channels
- Enable Multi-Factor Authentication (MFA): Secure your accounts with MFA to make them harder to breach
- Educate Yourself: Learn about common scam tactics to recognize them quickly
What to Do If You Receive a Scam Email
- Do Not Respond: Never reply or provide personal information.
- Mark as Spam: Flag the email to help your email provider filter similar scams.
- Report It: Notify your local cybersecurity authority or anti-fraud organization.
- Spread Awareness: Share your experience with friends and family to protect them.
Final Thoughts
Scammers rely on a lack of awareness to succeed. By staying informed and vigilant, you can avoid falling victim to their tactics. Remember, if something sounds too good to be true, it probably is.
If you’ve encountered similar scams or want to share tips for staying safe online, feel free to comment below!
TL;DR
In the previous tutorial I showed you how to get started with Vue.js 3 by building a Pokemon search application. I’m going to do the same here, but by using React.
In this post, you’ll learn how to use create-react-app (official React project scaffolding tool) to build a React application for searching Pokemon by using Poke API.
Introduction – The Battle is Over Indeed
A few years ago, this was a popular meme. It was funny because it was true 🙂
However, it seems that the craziness has settled down a bit and that the war is seemingly over. For now. These top four (top 5 lists are overrated 😉) frameworks established themselves:
I added the number of Github *
s (at the time of this writing), but I don’t want you to read into that too much 🤗
Analysis paralysis will, well, paralyze you!
You could argue that there are other options like Ember, Mithril, or good ‘ol jQuery even! However, this tweet says it all:
Developers are fighting over which frontend framework is better.. it’s not like users know or care. They mostly care about the User Experience. UX should be our focus on the frontend.
Personally, I stuck to the Angular bandwagon since version 1.0, but that started losing its ooomph after versions 2, 3, 4, … sorry, lost count.
In the previous post I said that Vue.js (due to its progressive and flexible nature) is a perfect fit for teams that have to rewrite old codebases one step at a time.
I also said that:
React’s JSX just seems wrong, and some people, smarter than me, swear by that being TheRightWay™, and I guess we’re not here to discuss tastes…
Well, after playing with it a bit, I can say it’s not that bad and that indeed one gets used to it.
Also, I recently checked out Svelte and kinda like it, so will be doing a post like this next; stay tuned.
General ‘framework war’ kind of questions I tend to answer in the following way:
Please just stop with the analysis paralysis already.
Do your research, pick a framework (any framework for that matter) that the community is using, use it, and see how far it gets you.
All these talks about X being slow or Y being better just make no sense until you try it yourself for your use case and preference.
Besides, nowadays speed will not be a deciding factor among the top JS frameworks.
With all this out of the way, fasten your seatbelts, take a venti (or trenta) sized cup of coffee, and let’s go do something practical! 💪
The Demo App
As said in the intro, we’ll build an application for searching (and showing) Pokemon by using Poke API.
You can fork the complete source code on Github, and you can see the app in action here.
Prerequisites
Make sure that you have the following tools installed:
- Node.js – here’s a free but outdated step by step guide/book for both Windows and Mac. Or, really, just go to the main website and download the executable for your machine
- Git – here’s a fun getting started tutorial in case you’re new to it
Start a new app with create-react-app
To start the project, run the following command (in your Terminal): npx create-react-app pokemon_search_react
This command, as their docs say, will install and execute create-react-app
, the official React project scaffolding tool. The output of that command will be similar to the one below:
Creating a new React app in /Users/nikola/Development/Testing/pokemon_search_react.
Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts with cra-template...
added 1490 packages in 2m
258 packages are looking for funding
run `npm fund` for details
Initialized a git repository.
Installing template dependencies using npm...
added 67 packages, and changed 1 package in 8s
262 packages are looking for funding
run `npm fund` for details
Removing template package using npm...
removed 1 package, and audited 1557 packages in 1s
262 packages are looking for funding
run `npm fund` for details
8 vulnerabilities (2 moderate, 6 high)
To address all issues (including breaking changes), run:
npm audit fix --force
Run `npm audit` for details.
Created git commit.
Success! Created pokemon_search_react at /Users/nikola/Development/Testing/pokemon_search_react
Inside that directory, you can run several commands:
npm start
Starts the development server.
npm run build
Bundles the app into static files for production.
npm test
Starts the test runner.
npm run eject
Removes this tool and copies build dependencies, configuration files
and scripts into the app directory. If you do this, you can’t go back!
We suggest that you begin by typing:
cd pokemon_search_react
npm start
Happy hacking!
Running our scaffolded project
Let’s run the commands (in terminal) noted at the end of the previous output:
cd pokemon_search_react
npm start
You should get this something similar to this output:
Compiled successfully!
You can now view pokemon_search_react in the browser.
Local: http://localhost:3001
On Your Network: http://192.168.8.165:3001
Note that the development build is not optimized.
To create a production build, use npm run build.
webpack compiled successfully
One of your dependencies, babel-preset-react-app, is importing the
"@babel/plugin-proposal-private-property-in-object" package without
declaring it in its dependencies. This is currently working because
"@babel/plugin-proposal-private-property-in-object" is already in your
node_modules folder for unrelated reasons, but it may break at any time.
babel-preset-react-app is part of the create-react-app project, which
is not maintianed anymore. It is thus unlikely that this bug will
ever be fixed. Add "@babel/plugin-proposal-private-property-in-object" to
your devDependencies to work around this error. This will make this message
go away.
⚠️ If you’re bothered by the seeming error-like output, then just add the following code to your package.json
file:
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.16.7"
},
You should see the following page in your browser if you open http://localhost:3001. Please note that the port (number after localhost:
) could be different. Usually it’s 3000, but I had some other stuff running at that port, so it took the next available one.
Folder structure
Now, let’s open this project in the editor of your choice (I’m using Visual Studio Code), and you should see something like this:
⚠️ This is an introduction tutorial, to get you running fast, so I won’t be going into any specific details and will be only focusing on the src
folder.
Add content
OK, so let’s add something to our app.
But, where to start? 🤔
Well, one of the first things I do when I come to a project for the first time is to look at the generated output. Then I try to find the strings corresponding to that output within the source code.
So, if you search for the string Learn React
, you’ll see the string is within the App.js
file. This file contains the following code:
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
Without knowing much about React, you can see where you would change the Learn React
text. So, let’s change that to Welcome to Pokemon Search
. Save that, and voila!, you’ll see the change reflected immediately in your browser.
If you’re new to web dev, you may not appreciate the usefulness of the so-called hot-reload. Back in the day, we had to refresh the browser after each save. Yeah, yeah, I know: #okboomer 🫣
Add some style
I borrowed the image (hope they don’t mind) from Wikipedia and saved it in the src
folder as logo.png
, added a search input, and styled it a bit to match it with the Pokemon style. In case you’re wondering, I asked ChatGPT to help with coming up with the style 🙂
Here’s the final code that I came up with in App.js
:
import logo from './logo.png';
import './App.css';
function App() {
return (
<div className="App">
<header>
<img alt="react logo" className="logo" src={logo} />
</header>
<main>
<div className="search-container">
<input className="search-box" type="text" placeholder="Search..." />
</div>
</main>
</div>
);
}
export default App;
And the CSS in the App.css
file:
body {
background: linear-gradient(to right, #FDDF3C, #3B4CCA);
}
header {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.logo {
margin: 0 2rem 0 0;
}
.search-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 10px;
}
.search-box {
width: 30%;
height: 50px;
font-size: 1.5em;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
margin: 40px 0;
text-align: center;
}
The way this looks now is as follows:
If you get stuck in any of the above steps, please reach out in the comments, and I’ll be happy to help.
Pokemon API
Finally, we come to the cool part 🥶, and that is to fetch some data from the PokeAPI and show it in our app.
How do we get this API? Well, if you do a simple Google search for pokemon api
and open the first link, you’ll also get to the documentation for their API.
By looking at the docs, you can find that the API endpoint that lists all the available Pokemon is this: https://pokeapi.co/api/v2/pokemon. There’s a total of 1302 records.
Now, fetching all this data every time you load your app would be a bad idea from the performance standpoint, but also from their fair use policy.
So, what we will do is open this link which will fetch all 1320 Pokemon endpoints. The output looks like this:
Now, save this output (just Command + S or Ctrl + s if you’re on Windows) to a local file that we’ll name pokemonapi.json
and place it in the src/
folder.
⚠️ I just want to give proper props to PokeAPI for their awesome service 👏
For our demo purposes, this will be fine, but if you’d like to deploy this somewhere where you’d get a lot of traffic (or, if the main list of Pokemon changes), you’d have to cache the responses. We’ll tackle this in another blog post, but just something to keep in mind.
Show the data
Now we’ll want to list all the Pokemon that we have in the pokemonapi.json
file. To do that, we’ll add some code at the top of the App.js
file that looks like this:
import pokemonData from "./pokemonapi.json";
import React, { useState } from "react";
And then, inside the function App()
:
const [pokemonList, setPokemonList] = useState(pokemonData.results);
const [searchTerm, setSearchTerm] = useState("");
const [selectedPokemon, setSelectedPokemon] = useState(null);
const filteredPokemonList = pokemonList.filter((pokemon) =>
pokemon.name.includes(searchTerm)
);
filteredPokemonList
function returns the list of pokemon that we loaded from the pokemonapi.json
file and filters them by what we enter in the input box.
We’ll add this portion of JSX just below the search-container
div:
<ul>
{filteredPokemonList.map((pokemon) => (
<li key={pokemon.id} className="pokemon-item">
<a href="#">{pokemon.name}</a>
</li>
))}
</ul>
And, we’ll add a bit of CSS:
.pokemon-item {
float: left;
margin: 10px;
}
.pokemon-item a {
color: #000;
text-decoration: none;
font-size: 16px;
transition: color 0.3s ease;
text-transform: capitalize;
}
.pokemon-item a:hover {
color: #3B4CCA;
}
ul {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 5px;
list-style: none;
}
Doing this, you should have your app look like this:
Get additional details about each Pokemon
If you open any of the URLs that you see in the pokemonapi.json
file, you will get an output like this:
To show some additional information about a certain Pokemon (once clicked on its name in the list), we’ll update the a
tag inside the li
:
<li key={pokemon.id} className="pokemon-item">
<a href="#" onClick={() => showPokemon(pokemon.url)}>{pokemon.name}</a>
</li>
On the click event we attached the showPokemon
function (by using onClick
) and passed it the URL that we get in the main API call (loaded from the JSON file).
Now we should define this function:
const showPokemon = async (url) => {
const response = await fetch(url);
if (!response.ok) {
console.error(`Error fetching Pokemon: ${response.statusText}`);
return;
}
const data = await response.json();
setSelectedPokemon(data);
}
To show the selected Pokemon, we’ll add a bit of JSX between the search-container
div and the ul
:
{selectedPokemon && (
<div className="pokemon-details">
<h2>{selectedPokemon.name}</h2>
<img
src={selectedPokemon.sprites.front_default}
alt={selectedPokemon.name}
/>
<p>Height: {selectedPokemon.height}</p>
<p>Weight: {selectedPokemon.weight}</p>
{selectedPokemon.stats.map((stat, index) => (
<div key={index}>
<p>
{stat.stat.name}: {stat.base_stat}
</p>
</div>
))}
</div>
)}
We’re using selectedPokemon &&
so that the line {{ selectedPokemon.name }}
doesn’t throw an error for trying to access a property that doesn’t exist on the object selectedPokemon
(which is initialized as null
in the beginning: const [selectedPokemon, setSelectedPokemon] = useState(null);
).
BTW, useState
is a so-called React Hook that essentially enables you to update a certain variable.
Anyways, back on point; we’re using map
to loop through all the elements of the stats
array and output the name
and base_stat
properties.
To make it look nicer, we’ll use a bit of CSS:
.pokemon-details {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 30%;
margin: 20px auto;
padding: 20px;
border: 1px solid #000;
border-radius: 10px;
color: #000;
text-transform: capitalize;
}
.pokemon-details img {
width: 100px;
height: 100px;
}
Two-way data binding
Right now if you test your app and type something in the search box, it will not filter the list of Pokemon.
You may remember that in Vue.js, we achieved this by using the v-model
. However, in React, there isn’t a direct equivalent to Vue’s v-model
for two-way data binding. In React we can achieve a similar effect by using a combination of state and event handlers.
To do this, we’ll update the input
tag to this:
<input className="search-box" type="text"
placeholder="Search..."
value={searchTerm}
onChange={event => setSearchTerm(event.target.value)}
/>
In this code, searchTerm
is a state variable that is set as the value of the input field. The onChange
event handler updates searchTerm
whenever the user types something into the input field. This creates a two-way data binding effect similar to Vue’s v-model.
Go try it out! The app is now fully functional 💪
All the code
For reference, for those following the tutorial to the dot, here’s the full listing of App.js
file:
import logo from "./logo.png";
import "./App.css";
import pokemonData from "./pokemonapi.json";
import React, { useState } from "react";
function App() {
const [pokemonList, setPokemonList] = useState(pokemonData.results);
const [searchTerm, setSearchTerm] = useState("");
const [selectedPokemon, setSelectedPokemon] = useState(null);
const filteredPokemonList = pokemonList.filter((pokemon) =>
pokemon.name.includes(searchTerm)
);
const showPokemon = async (url) => {
const response = await fetch(url);
if (!response.ok) {
console.error(`Error fetching Pokemon: ${response.statusText}`);
return;
}
const data = await response.json();
setSelectedPokemon(data);
};
return (
<div className="App">
<header>
<img alt="react logo" className="logo" src={logo} />
</header>
<main>
<div className="search-container">
<input className="search-box" type="text" placeholder="Search..."
value={searchTerm}
onChange={event => setSearchTerm(event.target.value)}
/>
</div>
{selectedPokemon && (
<div className="pokemon-details">
<h2>{selectedPokemon.name}</h2>
<img
src={selectedPokemon.sprites.front_default}
alt={selectedPokemon.name}
/>
<p>Height: {selectedPokemon.height}</p>
<p>Weight: {selectedPokemon.weight}</p>
{selectedPokemon.stats.map((stat, index) => (
<div key={index}>
<p>
{stat.stat.name}: {stat.base_stat}
</p>
</div>
))}
</div>
)}
<ul>
{filteredPokemonList.map((pokemon) => (
<li key={pokemon.id} className="pokemon-item">
<a href="#" onClick={() => showPokemon(pokemon.url)}>
{pokemon.name}
</a>
</li>
))}
</ul>
</main>
</div>
);
}
export default App;
And here’s the full contents of the App.css
file:
body {
background: linear-gradient(to right, #FDDF3C, #3B4CCA);
}
header {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.logo {
margin: 0 2rem 0 0;
}
.search-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 10px;
}
.search-box {
width: 30%;
height: 50px;
font-size: 1.5em;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
margin: 40px 0;
text-align: center;
}
.pokemon-item {
float: left;
margin: 10px;
}
.pokemon-item a {
color: #000;
text-decoration: none;
font-size: 16px;
transition: color 0.3s ease;
text-transform: capitalize;
}
.pokemon-item a:hover {
color: #3B4CCA;
}
ul {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 5px;
list-style: none;
}
.pokemon-details {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 30%;
margin: 20px auto;
padding: 20px;
border: 1px solid #000;
border-radius: 10px;
color: #000;
text-transform: capitalize;
}
.pokemon-details img {
width: 100px;
height: 100px;
}
.pokemon-details p {
margin: 5px;
}
B e a utiful
At this point, we can search for some Pokemon (my son’s favorite is, ofc, Pikachu), and if we click on it, we’ll get this:
Deployment
If you’d like to host this on your web server, then first run npm run build
and you’ll get an output similar to this:
> [email protected] build
> react-scripts build
Creating an optimized production build...
Compiled with warnings.
[eslint]
src/App.js
Line 64:15: The href attribute requires a valid value to be accessible. Provide a valid, navigable address as the href value. If you cannot provide a valid href, but still need the element to resemble a link, use a button and change it with appropriate styles. Learn more: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/HEAD/docs/rules/anchor-is-valid.md jsx-a11y/anchor-is-valid
Search for the keywords to learn more about each warning.
To ignore, add // eslint-disable-next-line to the line before.
File sizes after gzip:
58.34 kB (-1 B) build/static/js/main.b3bd969c.js
1.78 kB build/static/js/453.bd7a2879.chunk.js
677 B build/static/css/main.af098727.css
The project was built assuming it is hosted at /.
You can control this with the homepage field in your package.json.
The build folder is ready to be deployed.
You may serve it with a static server:
serve -s build
Find out more about deployment here:
https://cra.link/deployment
Now all you have to do is take the contents of the build
folder (you may remember that in Vue that folder was called dist
) and ‘paste’ it on your static web server.
If you don’t have a server of your own, then Vite has an extensive description for deploying your static pages to many popular services like Github Pages, Netlify, Vercel, Surge, etc.
You can deploy to Github Pages in under 2 minutes by following their documentation.
Just for brevity sake the steps are as follows:
- create a new public Github repository and name it
username.github.io
, whereusername
is yourusername
on GitHub. - clone the repo with
git clone https://github.com/username/username.github.io
- inside the folder copy the contents of the
build
folder - commit and push the changes:
git add --all
git commit -m "Initial commit"
git push -u origin main
Now your site will be visible online at https://username.github.io (again, where username
is your Github username)
You can see my deployment live here.
⚠️ If you’ll have multiple folders in Github pages, then before using the npm run build
command, you’ll want to set the homepage
variable in the package.json
file to that folder. Example: "homepage": "/pokemon-search-react"
.
Conclusion
In this tutorial, you learned how to get started with using React by building an application for searching Pokemon by using PokeAPI and making it publically accessible via Github Pages.
I intend to cover at least two more frameworks in the future blog posts, so stay tuned. Finally, out of curiosity, which one do you prefer so far: React or Vue.js? I’m looking to test Svelte next, but curious which one you’d like to see me cover.
Please leave any comments and feedback in the discussion section below, and thank you for reading!
TL;DR
Espanso is a text expander tool that will save you lots of repetitive typing. For real.
Ever had to repeatedly type your email address into a form? Check.
How about your name? Surname? Email signature? Check. Check. Check.
Well, instead of risking a typo — like sending that standout job application with the wrong email, and they never get back to you (even though you were certain you were the top pick among the multitude of MAANG folks recently let go—oops, went on a tangent there for a sec 🙂) — how about simply typing ;em
and watching it magically expand into your email?
Why Espanso?
I’m in no way affiliated with them, but kudos must be given where due.
Sure enough, there are tools like TextExpander, but Espanso stands out because it’s open source.
Installation
Espanso is cross-platform, with binaries for Windows, Mac, and Linux. I’m sure anyone can find their way around their Installation page: https://espanso.org/install/. If you’re on a Mac and using brew
, you can also install it from the command line: brew install espanso
.
Accessibility
To use Espanso on a Mac, you’ll need to grant Accessibility access via System Preferences → Security & Privacy → Privacy → Accessibility
.
Configuration
On a Mac, the Espanso config folder usually lives at ~/Library/Application\ Support/espanso
. The config
folder is for Espanso settings, while the match
folder (which contains the base.yml
file) is where your triggers and replacements should go.
Example
Just as an example, this is how mine looks like (feel free to take the logic behind emojis):
# espanso match file
# For a complete introduction, visit the official docs at: https://espanso.org/docs/
# You can use this file to define the base matches (aka snippets)
# that will be available in every application when using espanso.
# Matches are substitution rules: when you type the "trigger" string
# it gets replaced by the "replace" string.
matches:
# signatures
- trigger: ";n"
replace: "Nikola"
- trigger: ";b"
replace: "Brežnjak"
- trigger: ";li"
replace: "https://www.linkedin.com/in/nikola-bre%C5%BEnjak-892b9a24/"
- trigger: ";sn"
replace: "Kind regards,\nNikola Brežnjak\nhttp://www.nikola-breznjak.com/blog"
- trigger: ";web"
replace: "http://www.nikola-breznjak.com/"
- trigger: ";em"
replace: "[email protected]"
## git
- trigger: ";ga"
replace: "git add ."
- trigger: ";gb"
replace: "git branch"
- trigger: ";gc"
replace: "git commit -m "
- trigger: ";gd"
replace: "git diff --color "
- trigger: ";gf"
replace: "git fetch --all"
- trigger: ";gi"
replace: "find . -name '.DS_Store' -type f -delete"
- trigger: ";gl"
replace: "git log"
- trigger: ";gp"
replace: "git push origin main"
- trigger: ";gs"
replace: "git status"
- trigger: ";gt"
replace: "git remote -v"
- trigger: ";gu"
replace: "git pull origin main"
## blog
- trigger: ";bimp"
replace: "https://nikola-breznjak.com/blog/books/want-improve-read-books/"
- trigger: ";brem"
replace: "https://nikola-breznjak.com/blog/miscellaneou/make-remote-developer/"
## emojis
- trigger: ";eew"
replace: "⚠️"
- trigger: ";eet"
replace: "🤔"
- trigger: ";eeb"
replace: "💰"
- trigger: ";eem"
replace: "💪"
- trigger: ";eetm"
replace: "™"
- trigger: ";eeh"
replace: "❤️"
- trigger: ";eeu"
replace: "👍"
- trigger: ";eep"
replace: "🙏"
- trigger: ";eef"
replace: "🤦"
- trigger: ";ees"
replace: "🙂"
- trigger: ";eeg"
replace: "😎"
- trigger: ";eev"
replace: "👋"
- trigger: ";eel"
replace: "😂"
- trigger: ";eec"
replace: "👏"
- trigger: ";eeo"
replace: "✅"
- trigger: ";eer"
replace: "🚀"
- trigger: ";eex"
replace: "⏭️"
## replies
- trigger: ";ryw"
replace: "You’re welcome 👍"
- trigger: ";rlmk"
replace: "Please let me know 👍"
- trigger: ";rbtw"
replace: "Btw, how are things on your end?"
- trigger: ";rt"
replace: "Thank you! 👍"
## misc
- trigger: ";fd"
replace: "firebase deploy"
- trigger: ";wed"
replace: "Happy Wednesday (a dy on which, historically, most people wed on - thus: Wed nes day). Not really, but it would be a fun fact actually 🙂"
- trigger: ";cl"
replace: "console.log("
- trigger: ";se"
replace: "select * from "
- trigger: ";o"
replace: "open ."
- trigger: ";im"
replace: ""
vars:
- name: "clipb"
type: "clipboard"
- trigger: ";ch"
replace: "chrome://history"
- trigger: ";;c"
replace: "code ."
Beyond the Basics: Advanced Configurations
As you can see above, you can create simple trigger/replace combinations, but also in example of the ;im
trigger to insert an image directly from the clipboard in Markdown format.
They even support plugins, and there’s one for all the emojis you’d ever want, and the installation is straightforward.
Conclusion
Espanso is more than a tool; it’s a testament to the open-source community’s ability to create solutions that are both powerful and accessible. I wholeheartedly recommend you give it a try.
If you’re also using Espanso, and have some great tips to share, please leave them in the comments.
Thanks for reading, and hope it was useful 👋
TL;DR
In the previous tutorial I showed you how to get started with Vue.js 3 by building a Giphy search application. That was a long while ago 🙂, so here’s a new and updated post.
But, to not just update the post, I decided to do something different. Since my kids are into Pokemon, what better use of my coding skills than to make them an app where they can search for their favorite Pokemon and check their stats.
So, in this post, you’ll learn how to use create-vue (official Vue project scaffolding tool) to build a Vue.js 3 application for searching Pokemon by using Poke API.
Introduction – The Battle is Over Indeed
A few years ago, this was a popular meme. It was funny because it was true 🙂
However, it seems that the craziness has settled down a bit and that the war is seemingly over. For now. These top four (top 5 lists are overrated 😉) frameworks established themselves:
I added the number of Github *
s (at the time of this writing), but I don’t want you to read into that too much 🤗
Analysis paralysis will, well, paralyze you!
You could argue that there are other options like Ember, Mithril, or good ‘ol jQuery even! However, this tweet says it all:
Developers are fighting over which frontend framework is better.. it’s not like users know or care. They mostly care about the User Experience. UX should be our focus on the frontend.
Personally, I stuck to the Angular bandwagon since version 1.0, but that started losing its ooomph after versions 2, 3, 4, … sorry, lost count.
I believe that Vue.js (due to its progressive and flexible nature) is a perfect fit for teams that have to rewrite old codebases one step at a time. However, you can also use it as a full-blown framework if you wish.
Personally, React’s JSX just seems wrong, and some people, smarter than me, swear by that being TheRightWay™, and I guess we’re not here to discuss tastes…
Recently I checked out Svelte and kinda like it, so will be doing a post like this for it; stay tuned.
General ‘framework war’ kind of questions I tend to answer in the following way:
Please just stop with the analysis paralysis already.
Do your research, pick a framework (any framework for that matter) that the community is using, use it, and see how far it gets you.
All these talks about X being slow or Y being better just make no sense until you try it yourself for your use case and preference.
Besides, nowadays speed will not be a deciding factor among the top JS frameworks.
With all this out of the way, fasten your seatbelts, take a venti (or trenta) sized cup of coffee, and let’s go do something practical! 💪
The Demo App
As said in the intro, we’ll build an application for searching (and showing) Pokemon by using Poke API.
You can fork the complete source code on Github.
I’m using Vue.js 3, and I’ll be referring to it as just Vue in the rest of this post.
Prerequisites
Make sure that you have the following tools installed:
- Node.js – here’s a free but outdated step by step guide/book for both Windows and Mac. Or, really, just go to the main website and download the executable for your machine
- Git – here’s a fun getting started tutorial in case you’re new to it
Start a new app with create-vue
To start the project, run the following command (in your Terminal): npm create vue@latest
This command, as their docs say, will install and execute create-vue
, the official Vue project scaffolding tool. You will be presented with prompts for several optional features such as TypeScript and testing support (I named the project PokemonSearch_Vue3):
✔ Project name: … PokemonSearch_Vue3
✔ Package name: … pokemonsearch-vue3
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit Testing? … No / Yes
✔ Add an End-to-End Testing Solution? › No
✔ Add ESLint for code quality? … No / Yes
Scaffolding project in /Users/Nikola.Breznjak/Development/Web/Vue/PokemonSearch_Vue3...
Done. Now run:
cd PokemonSearch_Vue3
npm install
npm run dev
In the previous blog post, we used Vue CLI. That has been deprecated and create-vue
is now the default. The reasons that they quote are:
Vue CLI is based on webpack, while create-vue is based on Vite. Vite supports most of the configured conventions found in Vue CLI projects out of the box, and provides a significantly better development experience due to its extremely fast startup and hot-module replacement speed. Learn more about why we recommend Vite over webpack here.
Unlike Vue CLI, create-vue itself is just a scaffolding tool: it creates a pre-configured project based on the features you choose, and delegates the rest to Vite. Projects scaffolded this way can directly leverage the Vite plugin ecosystem which is Rollup-compatible.
You can learn more about ‘why’ Vite here.
Running our scaffolded project
Let’s run the commands (in terminal) noted in the previous output:
cd PokemonSearch_Vue3
npm install
npm run dev
You should get this output:
VITE v5.0.12 ready in 175 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
You should see the following page in your browser if you open http://localhost:5173.
Folder structure
Now, let’s open this project in the editor of your choice (I’m using Visual Studio Code + Volar extension), and you should see something like this:
This is an introduction tutorial, to get you running fast, so I won’t be going into any specific details and will be only focusing on the src
folder.
Add content
OK, so let’s add something to our app.
But, where to start? 🤔
Well, one of the first things I do when I come to a project for the first time is to look at the generated output. Then I try to find the strings corresponding to that output within the source code.
So, if you search for the string You did it!
, you’ll see the string is within the App.vue
file. This file contains the following code:
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
<style scoped>
header {
line-height: 1.5;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
}
</style>
Without knowing much about Vue.js, you can see where you would change the You did it!
text. So, let’s change that to Welcome to Pokemon Search
. Save that, and voila!, you’ll see the change reflected immediately in your browser.
Add some style
I borrowed the image (hope they don’t mind) from Wikipedia and saved it in the assets
folder as logo.webp
, added a search input, and styled it a bit to match it with the Pokemon style. In case you’re wondering, I asked ChatGPT to help with coming up with the style 🙂
Here’s the final code that I came up with in App.vue
:
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.webp" />
</header>
<main>
<div class="search-container">
<input class="search-box" type="text" placeholder="Search..." v-model="searchTerm">
</div>
</main>
</template>
<style scoped>
header {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.logo {
margin: 0 2rem 0 0;
}
.search-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 10px;
}
.search-box {
width: 30%;
height: 50px;
font-size: 1.5em;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
margin: 40px 0;
text-align: center;
}
Also, I did a few other smaller things:
- added
background: linear-gradient(to right, #FDDF3C, #3B4CCA);
in thebase.css
file in thebody
tag - removed the
main.css
file altogether - imported
base.css
in themain.js
file instead ofmain.css
- removed the whole
components
folder
The way this looks now is as follows:
If you get stuck in any of the above steps, please reach out in the comments, and I’ll be happy to help.
Pokemon API
Finally, we come to the cool part 🥶, and that is to fetch some data from the PokeAPI and show it in our app.
How do we get this API? Well, if you do a simple Google search for pokemon api
and open the first link, you’ll also get to the documentation for their API.
By looking at the docs, you can find that the API endpoint that lists all the available Pokemon is this: https://pokeapi.co/api/v2/pokemon. There’s a total of 1302 records.
Now, fetching all this data every time you load your app would be a bad idea from the performance standpoint, but also from their fair use policy.
So, what we will do is open this link which will fetch all 1320 Pokemon endpoints. The output looks something like this:
Now, save this output (just Command + S or Ctrl + s if you’re on Windows) to a local file that we’ll name pokemonapi.json
and place this file in the src/assets
folder.
⚠️ I just want to give proper props to PokeAPI for their awesome service 👏
For our demo purposes, this will be fine, but if you’d like to deploy this somewhere where you’d get a lot of traffic (or, if the main list of Pokemon changes), you’d have to cache the responses. We’ll tackle this in another blog post, but just something to keep in mind.
Show the data
Now we’ll want to list all the Pokemon that we have in the saved pokemonapi.json
file. To do that, we’ll write some Javascript code in the top of the App.vue
file that looks like this:
<script>
import pokemonData from './assets/pokemonapi.json';
export default {
data() {
return {
pokemonList: pokemonData.results,
searchTerm: '',
selectedPokemon: null
}
},
computed: {
filteredPokemonList() {
return this.pokemonList.filter(pokemon => pokemon.name.includes(this.searchTerm));
}
}
}
</script>
filteredPokemonList
function returns the list of pokemon that we loaded from the pokemonapi.json
file and filters them by what we enter in the input box.
And we’ll add this portion of HTML just below the search box:
<ul>
<li v-for="pokemon in filteredPokemonList" :key="pokemon.id" class="pokemon-item">
{{ pokemon.name }}
</li>
</ul>
And, we’ll add a bit of CSS:
.pokemon-item {
float: left;
margin: 10px;
}
.pokemon-item a {
color: #000;
text-decoration: none;
font-size: 16px;
transition: color 0.3s ease;
text-transform: capitalize;
}
.pokemon-item a:hover {
color: #3B4CCA;
}
ul {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 5px;
list-style: none;
}
Doing this, you should have your app look something like this:
Get additional details about each Pokemon
If you open any of the URLs that you see in the pokemonapi.json
file, you will get something like this:
To show some additional information about a certain Pokemon (once clicked on its name in the list), we’ll update the li
item by adding the a
link to it like this:
<li v-for="pokemon in filteredPokemonList" :key="pokemon.id" class="pokemon-item">
<a href="#" @click="showPokemon(pokemon.url)">{{ pokemon.name }}</a>
</li>
On the click event we attached the showPokemon
function (by using @click
) and passed it the URL that we get in the main API call (loaded from the JSON file).
Now we should define this function in the methods
object like this (I’m showing the contents of the whole script tag):
import pokemonData from './assets/pokemonapi.json';
export default {
data() {
return {
pokemonList: pokemonData.results,
searchTerm: '',
selectedPokemon: null
}
},
computed: {
filteredPokemonList() {
return this.pokemonList.filter(pokemon => pokemon.name.includes(this.searchTerm));
}
},
methods: {
async showPokemon(url) {
const response = await fetch(url);
if (!response.ok) {
console.error(`Error fetching Pokemon: ${response.statusText}`);
return;
}
this.selectedPokemon = await response.json();
console.log(this.selectedPokemon);
}
}
}
To show the selected Pokemon, we’ll add a bit of HTML:
<div class="pokemon-details" v-if="selectedPokemon">
<h2>{{ selectedPokemon.name }}</h2>
<img :src="selectedPokemon.sprites.front_default" alt="selectedPokemon.name">
<p>Height: {{ selectedPokemon.height }}</p>
<p>Weight: {{ selectedPokemon.weight }}</p>
<div v-for="(stat, index) in selectedPokemon.stats" :key="index">
<p>{{ stat.stat.name }}: {{ stat.base_stat }}</p>
</div>
</div>
We’re using v-if="selectedPokemon"
so that the line {{ selectedPokemon.name }}
doesn’t throw an error for trying to access a property that doesn’t exist on the object selectedPokemon
(which is initialized as null
in the beginnning).
We’re using v-for
to loop through all the elements of the stats
array and output the name
and base_stat
properties.
To make it look nicer, we’ll use a bit of CSS:
.pokemon-details {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 30%;
margin: 20px auto;
padding: 20px;
border: 1px solid #000;
border-radius: 10px;
color: #000;
text-transform: capitalize;
}
.pokemon-details img {
width: 100px;
height: 100px;
}
For reference, for those following the tutorial to the dot, here’s the full listing of HelloWorld.vue
file:
<script>
import pokemonData from './assets/pokemonapi.json';
export default {
data() {
return {
pokemonList: pokemonData.results,
searchTerm: '',
selectedPokemon: null
}
},
computed: {
filteredPokemonList() {
return this.pokemonList.filter(pokemon => pokemon.name.includes(this.searchTerm));
}
},
methods: {
async showPokemon(url) {
const response = await fetch(url);
if (!response.ok) {
console.error(`Error fetching Pokemon: ${response.statusText}`);
return;
}
this.selectedPokemon = await response.json();
console.log(this.selectedPokemon);
}
}
}
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.webp" />
</header>
<main>
<div class="search-container">
<input class="search-box" type="text" placeholder="Search..." v-model="searchTerm">
</div>
<div class="pokemon-details" v-if="selectedPokemon">
<h2>{{ selectedPokemon.name }}</h2>
<img :src="selectedPokemon.sprites.front_default" alt="selectedPokemon.name">
<p>Height: {{ selectedPokemon.height }}</p>
<p>Weight: {{ selectedPokemon.weight }}</p>
<div v-for="(stat, index) in selectedPokemon.stats" :key="index">
<p>{{ stat.stat.name }}: {{ stat.base_stat }}</p>
</div>
</div>
<ul>
<li v-for="pokemon in filteredPokemonList" :key="pokemon.id" class="pokemon-item">
<a href="#" @click="showPokemon(pokemon.url)">{{ pokemon.name }}</a>
</li>
</ul>
</main>
</template>
<style scoped>
header {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.logo {
margin: 0 2rem 0 0;
}
.search-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
/* Change the direction to row */
gap: 10px;
}
.search-box {
width: 30%;
height: 50px;
font-size: 1.5em;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
margin: 40px 0;
text-align: center;
}
.pokemon-item {
float: left;
margin: 10px;
}
.pokemon-item a {
color: #000;
text-decoration: none;
font-size: 16px;
transition: color 0.3s ease;
text-transform: capitalize;
}
.pokemon-item a:hover {
color: #3B4CCA;
}
ul {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 5px;
list-style: none;
}
.pokemon-details {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 30%;
margin: 20px auto;
padding: 20px;
border: 1px solid #000;
border-radius: 10px;
color: #000;
text-transform: capitalize;
}
.pokemon-details img {
width: 100px;
height: 100px;
}
</style>
B e a utiful
At this point, we can search for some Pokemon (my son’s favorite is, ofc, Pikachu), and if we click on it, we’ll get something like this:
Deployment
If you’d like to host this on your web server, then first run npm run build
and you’ll get an output similar to this:
Now all you have to do is take the contents of the dist
folder and ‘paste’ it on your static web server.
If you don’t have a server of your own, then Vite has an extensive description for deploying your static pages (if you don’t have the ) to many popular services like Github Pages, Netlify, Vercel, Surge, etc.
You can deploy to Github Pages in under 2 minutes by following their documentation.
Just for brevity sake the steps are as follows:
- create a new public Github repository and name it
username.github.io
, whereusername
is yourusername
on GitHub. - clone the repo with
git clone https://github.com/username/username.github.io
- inside the folder copy the contents of the
dist
folder - commit and push the changes:
git add --all
git commit -m "Initial commit"
git push -u origin main
Now your site will be visible online at https://username.github.io (again, where username
is your Github username)
You can check my deployment here.
Conclusion
In this tutorial, you learned how to get started with using Vue.js 3 by building an application for searching Pokemon by using PokeAPI and making it publically accessible via Github Pages.
Please leave any comments and feedback in the discussion section below, and thank you for reading!
TL;DR
If you ever get an error that you can’t save a file in Visual Studio Code, and it prompts you to ‘Retry as Admin’ (where you then have to enter your admin password) then the solution is super easy by using the chown
command.
Namely, you just have to run sudo chown -R $USER .
in the folder where your file is located and that will ‘fix it’.
Yep, not much else to it really 💪
Why does this happen?
Now, this may have happened if you were using sudo
when installing some Node.js packages with npm
(or some other package manager), or if you created the folder (not sure why, but still plausible) using the sudo
command.
A bit more about the chown command
The chown
command in UNIX and Linux-based systems (including MacOS), stands for ‘change owner’ and it’s used to change the ownership of files and directories.
A detailed breakdown of the above command is as follows:
- sudo: shorthand for ‘superuser do’; it grants administrative privileges for the command that follows. And yes, you’re right, you should use this command with caution
- chown: the command to change file’s owner
–R: this option makes the command recursive, which means it will apply to all files and directories within the current directory - $USER: This is a variable that represents the current logged-in user (aka you). By using $USER, you’re changing the ownership of the files to yourself, eliminating the need for using
sudo
later - .: the dot represents the current directory. So, the command affects the files and directories where it’s executed
Thread with caution
It’s important to understand why you’re using chown
, because incorrect usage can lead to permission issues or security risks. Generally, use it when you need to restore file ownership to the correct user after it’s been changed (often inadvertently). Be very vary of following tutorials (or any other instructions) that tell you to set the file permissions to anything else but you ($USER).
Learn even more about chown
If you want to learn more about the chown
command and its options, you can run the man chown
command in your terminal and you’ll get way more detailed documentation. Or, of course, just Google.
Hope this quick tip helps 🙌
TL;DR
A new phishing scam is targeting OpenSea users through email. Exercise caution and stay vigilant.
!TL;DR
I recently encountered an email that surprisingly bypassed my spam filters. The subject line read: "Your asset has recently received a new deal [C02aaA]".
The email included an image, showcasing what appeared to be a legitimate notification from OpenSea:
The email read:
New Offer Detected
Your asset has recently received a new deal.
Details:
Offer TxID: C02aaA
Buyer ID: Jamesmatic02
Review The Offer
This email is being sent to you as you have agreed to receive information from us. If you wish to stop receiving this type of information, please opt-out using the available unsubscribe option. Kindly avoid responding directly to this email. For any questions, reach out to our Support Center using the provided contact details.
Best regards.
Update your email preferences or unsubscribe here
© 2024 Sale Team
228 Park Ave S, #29976, New York, New York 10003, United States
beehiiv logoPowered by beehiiv
Clicking the Review The Offer
button redirects users to a phishing site, an exact replica of OpenSea, which then prompts for login credentials, effectively stealing them.
How to Protect Yourself
In situations like these, here are some crucial steps to ensure your online safety:
- Verify the Email Sender
- Always check the sender’s email address. Phishing attempts often come from email addresses that are misspelled or use different domains.
- Inspect the Domain Link
- Be meticulous about the URL. Phishing sites usually have URLs closely resembling the legitimate site, with subtle misspellings or different domain extensions. For instance, instead of https://opensea.io/, it might be https://openseasecure.io/
- Avoid Clicking Suspicious Links
- If an email asks you to click a link, hover over it first to preview the URL. If it looks suspicious, do not click it.
- Use Two-Factor Authentication
- Always enable two-factor authentication (2FA) on your accounts if possible, as this adds an extra layer of security
- Regularly Update Your Passwords
- Change your passwords frequently and avoid using the same password across different platforms.
- Install Security Software
- Use reliable anti-virus and anti-malware software. These can often detect and alert you to suspicious websites and emails.
- Check Official Websites Directly
- If you receive an unexpected offer or alert, go directly to the official website by typing the URL into your browser, rather than clicking on links in emails.
Conclusion
By staying informed and cautious, you can significantly reduce the risk of falling victim to phishing attacks. Remember, in the digital realm, vigilance is your best defense. Stay safe out there!
⚠️ Disclaimer: This post isn’t sponsored—I’m just a fan of the product. I recently encountered a small hiccup (probably my own doing) with my Sparkly toy and wanted to share a straightforward solution I discovered.
Sparkly is a fantastic STEM toy created by a local team (a quick Google search will tell you more). My journey with Sparkly began with excitement as I assembled it, eager to see it in action. I switched on my phone’s flashlight for the grand test, only to realize something was amiss. It seemed I had inadvertently set up the motor incorrectly.
Here’s what happened: one wheel was rotating correctly, moving forward, but its counterpart was rebelliously spinning in the opposite direction, backward. Now, you may expect that these motors operate on direct current. The solution, in theory, was simple: reverse the polarity by swapping the + and – connections.
However, when I tried to switch the battery input, I hit a snag. The stubborn cable, as shown in the image, refused to budge:
Refusing to be defeated, I took a more hands-on approach. I unsoldered the black and red wires on the motor and then switched their positions.
And voilà, it worked! What seemed like a complicated problem turned out to have a pretty simple fix.
So, to all my fellow STEM enthusiasts and tinkerers, remember, sometimes the solution is just a wire swap away 🙂
Happy tinkering and happy holidays! 👋
TL;DR
This is the second part of the "This is How Your Parents Used to Build Websites". The first blog post is here.
Now, if you thought our HTML journey was exciting, buckle up! We’re about to dive into the world of CSS and JavaScript. These are the tools that will transform your static HTML pages into vibrant, interactive experiences.
Ready? Let’s go!
Part 1: CSS – Making Your Website Stylish
Basic Concepts of CSS
This is how a simple CSS statement would look like: p { color: blue};
.
There are three basic concepts of CSS:
- Selectors:
- Think of them as the ‘who’ in styling.
- For example,
p { }
targets all<p>
elements.
- Properties and Values:
- The ‘what’ and ‘how’. They go withing the
{}
brackets. - Like
color: blue;
– this tells the paragraph text to be blue.
- The ‘what’ and ‘how’. They go withing the
Incorporating CSS into HTML
There are three ways you can add CSS into HTML:
- Inline Styles: Directly in an HTML tag, like
<p style="color: blue;">
. - Internal Styles: Inside the
<style>
tag in the HTML head. - External Stylesheets: A separate
.css
file, linked in the HTML head with<link rel="stylesheet" href="style.css">
.
Exploring Common CSS Properties
Color and Fonts
body { color: #333; font-family: 'Arial', sans-serif; }
Font Size, Weight, and Style
p {
font-size: 16px;
font-weight: bold;
font-style: italic;
}
Text Alignment and Decoration
h1 {
text-align: center;
text-decoration: underline;
}
Margin and Padding
.content {
margin: 20px;
padding: 15px;
}
Borders
.box {
border: 2px solid #000000; /* Solid black border */
border-radius: 10px; /* Rounded corners */
}
Styling Links
-
Hover Effect
a:hover { color: #ff0000; /* Red color on hover */ text-decoration: none; }
-
Link Visited State
a:visited { color: #800080; /* Purple for visited links */ }
Layout with Flexbox
display: flex;
enables a flexible box layout.- There are games that explain flexbox, so if you’re curious go check them out.
Responsive Design Basics
In order to adapt the site to various screen sizes and make sure it looks good on an iPhone as well as on your laptop, we can use the so-called media queries:
@media (max-width: 600px) { body { background-color: lightblue; }}
This changes the background color on screens smaller than 600px.
Advanced Selectors
-
Pseudo-classes
a:hover { color: red; }
changes link color on hover -
CSS Transitions
transition: background-color 0.5s ease;
-
CSS Animations
@keyframes example { from {background-color: red;} to {background-color: yellow;} } div { animation-name: example; animation-duration: 4s; }
Advanced CSS
If you’re curious enough, please explore SASS (CSS with superpowers) on your own.
Part 2: JavaScript – Bringing Interactivity to Your Website
JavaScript is the scripting language that lets you create dynamic content.
Basic Syntax and Concepts
Variables
let message = 'Hello, world!';
– with this we defined the variable called ‘message’ and saved in it the string ‘Hello, world!’.
You may come across the keyword var
, but that was an old keyword that is no longer suggested to be used because of the unintended consequences.
Functions
function greet(name) {
alert('Hello, ' + name);
}
With this we created a function called greet
that takes a parameter called name
and outputs Hello name
via an alert (OFC, name is replaced with whatever you pass into the function as a parameter).
Events
<button onclick="greet('Alice')">Greet</button>
With onclick
we define what function is going to be run when the button is clicked.
DOM Manipulation
-
Change text
document.getElementById('myText').innerHTML = 'New Text';
-
A button that changes a theme color
<button onclick="changeColor()">Change Theme</button> <script> function changeColor() { document.body.style.backgroundColor = 'lightblue'; } </script>
-
Changing styles on hover using JavaScript
document.getElementById('myElement').onmouseover = function() { this.style.color = 'red'; };
-
Interactive portfolio enhancements
document.getElementById('project1').onmouseover = function() { this.style.transform = 'scale(1.1)'; }; document.getElementById('project1').onmouseout = function() { this.style.transform = 'scale(1)'; };
-
Handling a click event
document.getElementById('myButton').addEventListener('click', function() { alert('Button clicked!'); });
Advanced topics
Using fetch
to load data
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));
I wrote a three series blog post on doing so-called AJAX calls (fetching data from some API), check them out here:
- Making AJAX calls in pure JavaScript
- Making AJAX calls using jQuery
- Making AJAX calls using the Fetch API
JavaScript ES6
There are some awesome things that have been added to the ‘new’ flavor of JavaScript, called ES6. I won’t go into the details here, but use Google (or ChatGPT, or Bard, or …) and look into these topics:
- Arrow Functions:
const add = (a, b) => a + b;
- Template Literals
const greeting = `Hello, ${name}!`;
Conclusion
And that’s a wrap! We’ve just scratched the surface of the amazing world of CSS and JavaScript. Remember, practice is key, and the web is your canvas. Go out there and create something awesome!
In the next blog post we’ll cover frameworks for CSS and JavaScript, pick one for each and create a Pokemon search application.
TL;DR
Hey future web wizards! Are you ready to embark on your journey into the vast world of web development? This post is your beginner-friendly guide to HTML. We’ll break down the essentials, infuse some humor, and provide plenty of examples for you to experiment with. By the end of this, you’ll be well on your way to crafting your first web page. Let’s dive into the world of tags and elements and make them your new best friends!
The intro you just read might sound like a blast from the past – a time when all aspiring web developers had were books and a computer (if they were lucky). Back then, the web was a simpler place, primarily text and links.
But hold on, we’re not just reminiscing about the old days. Even though web development has evolved tremendously (and yes, it’s a lot cooler now), understanding the basics of HTML is still crucial. So, even if you’re planning to leap into more advanced topics (which we’ll cover in the next blog post), this article is worth your time to grasp the fundamentals of HTML tags.
In this blog post we’ll cover HTML basics. In the next one we’ll touch on CSS and JavaScript.
Setting the Scene
Picture this: It’s your first day as an aspiring web developer, buzzing with ideas and ready to make your mark on the internet. But there’s a key skill you need to master first: HTML. Don’t fret – it’s far from daunting. HTML is the skeleton of all websites, and together, we’re going to unravel its mysteries.
Why HTML, Anyway?
HTML stands for HyperText Markup Language. It’s the skeleton of all web pages. Think of it as the foundation of a house – without it, you can’t build anything stable. HTML is where every web developer begins their journey. It’s simple, powerful, and the stepping stone to more complex web technologies.
OK, but What’s in a Markup Language?
Markup languages are all about annotating text to give it structure and meaning. HTML does this by using tags to turn plain text into a structured, visually appealing web page. It’s like taking a plain old document and adding magic to make it a web page.
⚠️ And, yes, to settle the debate: No, HTML is not programming. Sorry, not sorry.
Your First HTML Tags
Let’s start with the basics. Every HTML document has some key tags:
<!DOCTYPE html>
: This declaration defines the document type and HTML version<html>
: The root element that wraps the entire content<head>
: This contains meta-information about the document, like its title<body>
: The meat of the page – this is where all your content will go
Creating Your First HTML Page
Open up your favorite text editor (Visual Studio Code, Sublime Text, Vim, … – take your pick) and let’s get coding. Copy the following code into a file and name it index.html
:
<!DOCTYPE html>
<html>
<head>
<title>My First Web Page</title>
</head>
<body>
<h1>Hello, World!</h1>
<p>Welcome to my first web page.</p>
</body>
</html>
Now open this index.html
file in your favorite browser (Chrome, Safari, Firefox, …). Usually, just doubleclicking the file should make it automatically open in your default browser.
Voila! You’ve just created your first web page that should look like this:
Understanding the HTML Structure
HTML documents are structured as a tree of elements. Each element has an opening tag (<tag>
) and a closing tag (</tag>
). Some elements are self-closing, like <img />
(for inserting images) and <br />
(for inserting a new line). Think of it as a family tree, but for your web content.
Dive into Elements and Attributes
HTML elements can have attributes that provide additional information about the element. For example, <a href="http://www.nikola-breznjak.com/">My site</a>
uses the href
attribute to define the link’s destination.
Text Formatting and Layout
HTML offers a range of elements for formatting text:
<h1>
to<h6>
: Headings from most to least important<p>
: Paragraphs<strong>
and<em>
: Bold and italic text for emphasis<ul>
and<ol>
: Unordered (bullets) and ordered (numbers) lists<li>
: List items<div>
: A generic container for content, great for layout and styling purposes
Adding Images
To add an image, use <img src="image.jpg" alt="Description">
. Change the image.jpg
with an URL of an actual image. For example, edit the index.html file to look like this:
<!DOCTYPE html>
<html>
<head>
<title>My First Web Page</title>
</head>
<body>
<h1>Hello, World!</h1>
<p>Welcome to my first web page.</p>
<img src="https://i.imgur.com/AoX2x7f.png" alt="VIM for Life">
</body>
</html>
And you should get this:
Adding Links
To create a link, use the a
tag. Example: <a href="http://www.google.com">Link to Google.com</a>
.
Forms and User Input:
Forms are what you should use to get some input from users. Use the <form>
element to create an input area. Inside, use <input>
, <textarea>
, <button>
, and other form elements to gather user input.
For example, here’s the usual login form:
<form action="/submit-your-login-form" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<button type="submit">Login</button>
</form>
If you add that to the index.html
file, you should see this in your browser (when you refresh the page):
Embedding Media
You can embed videos and audio in HTML5 using <video>
and <audio>
tags. Here’s an example for video:
<video width="320" height="240" controls>
<source src="movie.mp4" type="video/mp4">
<source src="movie.ogg" type="video/ogg">
Your browser does not support the video tag.
</video>
and here’s the example for audio:
<audio controls>
<source src="audio.mp3" type="audio/mpeg">
<source src="audio.ogg" type="audio/ogg">
Your browser does not support the audio element.
</audio>
Of course, you would have to have the video and audio formats to try this out. You can Google, or create some of your own. Or, for the sake of quickly testing this out, here’s a link to an MP3 of a crowbar sound from a cult game Half Life.
Building a Simple Personal Portfolio Site with HTML
Let’s enhance our basic HTML skills by creating a simple personal portfolio page. This website will have four main sections: a header, an about me section, a projects section, and a contact section.
Creating the Basic Structure
Start by defining the structure of your page. Open your text editor and create a new file named portfolio.html
. Here’s how you’ll structure it:
<!DOCTYPE html>
<html>
<head>
<title>My Personal Portfolio</title>
</head>
<body>
<header>
<!-- Header content goes here -->
</header>
<section id="about">
<!-- About me content goes here -->
</section>
<section id="projects">
<!-- Projects list goes here -->
</section>
<section id="contact">
<!-- Contact information goes here -->
</section>
</body>
</html>
Crafting the Header
The header will contain your name and a navigation menu.
<header>
<h1>John Doe</h1>
<nav>
<ul>
<li><a href="#about">About Me</a></li>
<li><a href="#projects">Projects</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</header>
The About Me Section
Here, you’ll introduce yourself. Include a brief paragraph and maybe a profile picture.
<section id="about">
<h2>About Me</h2>
<img src="profile.jpg" alt="John Doe" />
<p>Hi, I'm John Doe, a passionate web developer...</p>
</section>
Showcasing Projects
In the projects section, list some of the work you’ve done. Use <ul>
for the list and <li>
for each project.
<section id="projects">
<h2>Projects</h2>
<ul>
<li><a href="http://example.com/project1">Project 1</a></li>
<li><a href="http://example.com/project2">Project 2</a></li>
<!-- Add more projects here -->
</ul>
</section>
The Contact Section
Include your contact information or a form for visitors to reach out to you.
<section id="contact">
<h2>Contact Me</h2>
<p>Email: [email protected]</p>
<!-- You can add a contact form here in future -->
</section>
Adding Some Styling
While CSS is primarily used for styling, you can add inline styles directly in your HTML to change the look of your page. For example:
<style>
body {
font-family: Arial, sans-serif;
}
header, section {
margin-bottom: 20px;
}
nav ul {
list-style-type: none;
}
nav ul li {
display: inline;
margin-right: 10px;
}
</style>
Place this <style>
tag within the <head>
section of your HTML.
Test and Iterate
This is how the full portfolio.html
file should look like:
<!DOCTYPE html>
<html>
<head>
<title>My Personal Portfolio</title>
<style>
body {
font-family: Arial, sans-serif;
}
header, section {
margin-bottom: 20px;
}
nav ul {
list-style-type: none;
}
nav ul li {
display: inline;
margin-right: 10px;
}
</style>
</head>
<body>
<header>
<h1>John Doe</h1>
<nav>
<ul>
<li><a href="#about">About Me</a></li>
<li><a href="#projects">Projects</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</header>
<section id="about">
<h2>About Me</h2>
<img src="profile.jpg" alt="John Doe" />
<p>Hi, I'm John Doe, a passionate web developer...</p>
</section>
<section id="projects">
<h2>Projects</h2>
<ul>
<li><a href="http://example.com/project1">Project 1</a></li>
<li><a href="http://example.com/project2">Project 2</a></li>
<!-- Add more projects here -->
</ul>
</section>
<section id="contact">
<h2>Contact Me</h2>
<p>Email: [email protected]</p>
<!-- You can add a contact form here in future -->
</section>
</body>
</html>
Open it up in a browser to see your work. It won’t look like a polished website yet, but you’ll see all your content structured according to your HTML.
Conclusion
Congratulations! You’ve just built a simple yet functional personal portfolio site using HTML. This is just the beginning of your journey into web development. As you learn more about HTML, CSS, and JavaScript, you’ll be able to enhance your site with better styling and functionality. Keep experimenting, keep learning, and most importantly, have fun with it!
In the next blog post we’ll cover CSS and JavaScript.
Recent posts
Categories
- Android (3)
- Books (114)
- Programming (22)
- CodeProject (35)
- Daily Thoughts (72)
- Go (3)
- iOS (5)
- JavaScript (127)
- Leadership (1)
- Meetups (8)
- Miscellaneou$ (77)
- Breaking News (8)
- CodeSchool (2)
- Hacker Games (3)
- Pluralsight (7)
- Projects (2)
- Sublime Text (2)
- PHP (6)
- Quick tips (40)
- Servers (8)
- Stack Overflow (81)
- Unity3D (9)
- Windows (8)
- Wordpress (2)