Welcome to this comprehensive tutorial on creating a single-page SvelteKit application from scratch. We'll construct a practical, fully functional mockup of an iOS app designed to calculate the ideal coffee-to-water brewing ratio. Along the way, you'll learn about building various components, understanding their interdependencies, and seeing the bigger picture of app development.
Ensure you have the following tools:
- Homebrew: The package manager for macOS (or Linux)
- Git: A distributed version control system
- Node.js and npm: A JavaScript runtime and package manager
- Visual Studio Code: A source code editor (or your preferred choice)
If you haven't installed these, refer to the links in the resources section at the end of this tutorial. Once you're all set, we can start creating your SvelteKit project.
Navigate to your desired directory in the terminal and run:
npm init svelte@next coffee-calc
This will initiate a new SvelteKit project named coffee-calc
. You can replace this with any name of your choice.
Navigate into your project directory:
cd coffee-calc
Then, install the project dependencies:
npm install
Start the development server:
npm run dev
Visit http://localhost:5000
in your web browser. You should see a "Hello world!" message, indicating that your SvelteKit project is set up correctly.
Our coffee brewing calculator will consist of several components. Let's build each of them:
- RatioInput.svelte: This component allows users to input the desired ratio of coffee to water.
- MassInput.svelte: This component lets users input the mass of coffee or water.
- UnitToggle.svelte: This component allows users to switch between using grams and ounces.
- FeedbackDisplay.svelte: This component displays the selected mass of coffee and water in the chosen unit.
- ResetButton.svelte: This component allows users to reset each input individually.
- ResetAllButton.svelte: This component allows users to reset all inputs at once.
- VisualFeedback.svelte: This component offers a visual cue to users when a value changes.
- KeyboardInput.svelte: This component allows the application to respond to keyboard inputs.
I'll provide the code for each component in this tutorial. As we build these components, we'll also discuss the role of each one.
Create a new file named RatioInput.svelte
in the src/lib
directory and add the following code:
<script>
export let ratio = { coffee: 1, water: 15 };
function handleInputChange(event) {
let increment = event.shiftKey ? 10 : 1;
let newValue = Number(event.target.value);
if (event.target.valueAsNumber >= 0) {
ratio[event.target.name] = newValue + increment;
} else if (event.target.valueAsNumber < 0) {
ratio[event.target.name] = newValue - increment;
} else {
ratio[event.target.name] = newValue;
}
}
</script>
<div class="ratio-input">
<label for="coffee">Coffee Ratio:</label>
<input id="coffee" name="coffee" bind:value={ratio.coffee} type="number" min="0" step="0.1" on:input={handleInputChange} />
<label for="water">Water Ratio:</label>
<input id="water" name="water" bind:value={ratio.water} type="number" min="0" step="0.1" on:input={handleInputChange} />
</div>
Create a new file named MassInput.svelte
in the src/lib
directory and add the following code:
<script>
export let mass = { coffee: 0, water: 0 };
function handleInputChange(event) {
let increment = event.shiftKey ? 10 : 1;
let newValue = Number(event.target.value);
if (event.target.valueAsNumber >= 0) {
mass[event.target.name] = newValue + increment;
} else if (event.target.valueAsNumber < 0) {
mass[event.target.name] = newValue - increment;
} else {
mass[event.target.name] = newValue;
}
}
</script>
<div class="mass-input">
<label for="coffee">Coffee Mass:</label>
<input id="coffee" name="coffee" bind:value={mass.coffee} type="number" min="0" step="0.1" on:input={handleInputChange} />
<label for="water">Water Mass:</label>
<input id="water" name="water" bind:value={mass.water} type="number" min="0" step="0.1" on:input={handleInputChange} />
</div>
Create a new file named UnitToggle.svelte
in the src/lib
directory and add the following code:
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
let units = 'grams';
function handleToggle() {
units = units === 'grams' ? 'ounces' : 'grams';
dispatch('unitChange', units);
}
</script>
<button on:click={handleToggle}>
{units}
</button>
Create a new file named FeedbackDisplay.svelte
in the src/lib
directory and add the following code:
<script>
export let mass = { coffee: 0, water: 0 };
export let units = 'grams';
</script>
<div class="feedback-display">
<p>You will need {mass.coffee} {units} of coffee and {mass.water} {units} of water.</p>
</div>
Create a new file named ResetButton.svelte
in the src/lib
directory and add the following code:
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function handleReset() {
dispatch('reset');
}
</script>
<button on:click={handleReset}>
Reset
</button>
Create a new file named ResetAllButton.svelte
in the src/lib
directory and add the following code:
<script>
import { createEventDispatcher } from 'svelte
';
const dispatch = createEventDispatcher();
function handleResetAll() {
dispatch('resetAll');
}
</script>
<button on:click={handleResetAll}>
Reset All
</button>
Create a new file named VisualFeedback.svelte
in the src/lib
directory and add the following code:
<script>
export let ratio = { coffee: 1, water: 15 };
</script>
<div class="visual-feedback">
<p>The ratio of coffee to water is {ratio.coffee}:{ratio.water}.</p>
</div>
Create a new file named KeyboardInput.svelte
in the src/lib
directory and add the following code:
<script>
import { onMount } from 'svelte';
onMount(() => {
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
});
function handleKeyDown(event) {
let increment = event.shiftKey ? 10 : 1;
switch (event.key) {
case 'ArrowUp':
// Increase the value by the increment
break;
case 'ArrowDown':
// Decrease the value by the increment
break;
}
}
</script>
Remember, you'll need to define what "value" refers to in the 'ArrowUp' and 'ArrowDown' cases of your handleKeyDown
function.
This concludes the process of building all the components. We'll use these components to create the CoffeeCalc.svelte
component, which is the main component of our application.
Now that we've built all the components, let's assemble them into the CoffeeCalc.svelte
component. Create a new file in the src/routes
directory named CoffeeCalc.svelte
and add the following code:
<script>
import InputComponent from './InputComponent.svelte';
import ToggleComponent from './ToggleComponent.svelte';
import FeedbackComponent from './FeedbackComponent.svelte';
import ResetComponent from './ResetComponent.svelte';
import VisualFeedbackComponent from './VisualFeedbackComponent.svelte';
import KeyboardComponent from './KeyboardComponent.svelte';
let coffeeMass = 17; // default coffee mass
let waterMass = 255; // default water mass
let coffeeRatio = 1; // coffee ratio is always 1
let waterRatio = waterMass / coffeeMass; // calculate water ratio
let ratio = { coffee: coffeeRatio, water: waterRatio };
let mass = { coffee: coffeeMass, water: waterMass };
let units = 'grams';
// Reactive statement to recalculate ratio when mass changes
$: {
coffeeMass = mass.coffee;
waterMass = mass.water;
waterRatio = parseFloat((waterMass / coffeeMass).toFixed(1));
ratio = { coffee: coffeeRatio, water: waterRatio };
}
function handleChange(event) {
units = event.detail;
}
</script>
<svelte:head>
<title>Home</title>
<meta name="description" content="Svelte demo app" />
</svelte:head>
<section>
<InputComponent bind:ratio bind:mass bind:units/>
<ToggleComponent on:change={handleChange} />
<FeedbackComponent {ratio} {mass} {units} />
<ResetComponent />
<VisualFeedbackComponent />
<KeyboardComponent />
</section>
<style>
section {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex: 0.6;
}
h1 {
width: 100%;
}
</style>
This CoffeeCalc.svelte
component imports all the individual components we built earlier and assembles them. It also handles the events dispatched by these components.
Svelte allows you to add styles to your components using CSS. You can add a <style>
tag inside your .svelte
component files to add styles. The styles you define inside a component only apply to that component, thanks to Svelte's CSS scoping. This is a huge advantage when building complex applications, as it prevents styles from leaking into other components unintentionally.
You can add styles to your components as per your design needs. For the purposes of this tutorial, we will not be going into the specifics of styling each component.
Congratulations! You've now built a functional single-page coffee brewing calculator using SvelteKit. This tutorial provided a comprehensive guide to SvelteKit application development, covering everything from setting up the project to creating individual components and assembling them.
Remember, the key to learning and mastering any technology is practice. Don't hesitate to modify this project, add new features, or even start a completely new project using the knowledge you've gained from this tutorial.
- Homebrew: https://brew.sh/
- Git: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
- Node.js and npm: https://nodejs.org/en/download/
- Visual Studio Code: https://code.visualstudio.com/download
- SvelteKit: https://kit.svelte.dev/