Skip to content

Instantly share code, notes, and snippets.

@chriscorwin
Created August 2, 2023 17:25
Show Gist options
  • Save chriscorwin/03c6154c0a3e801dfe5b48c3e71e4afc to your computer and use it in GitHub Desktop.
Save chriscorwin/03c6154c0a3e801dfe5b48c3e71e4afc to your computer and use it in GitHub Desktop.
SvelteKit Coffee Brewing App Tutorial for Beginners 2

In-Depth SvelteKit Coffee Brewing App Tutorial for Beginners

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.

Prerequisites

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.

Setting Up the Project

Step 1: Creating a new 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.

Step 2: Installing Project Dependencies

Navigate into your project directory:

cd coffee-calc

Then, install the project dependencies:

npm install

Step 3: Starting the Development Server

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.

Building the Components

Our coffee brewing calculator will consist of several components. Let's build each of them:

  1. RatioInput.svelte: This component allows users to input the desired ratio of coffee to water.
  2. MassInput.svelte: This component lets users input the mass of coffee or water.
  3. UnitToggle.svelte: This component allows users to switch between using grams and ounces.
  4. FeedbackDisplay.svelte: This component displays the selected mass of coffee and water in the chosen unit.
  5. ResetButton.svelte: This component allows users to reset each input individually.
  6. ResetAllButton.svelte: This component allows users to reset all inputs at once.
  7. VisualFeedback.svelte: This component offers a visual cue to users when a value changes.
  8. 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.

Building the RatioInput.svelte Component

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>

Building the MassInput.svelte Component

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>

Building the UnitToggle.svelte Component

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>

Building the FeedbackDisplay.svelte Component

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>

Building the ResetButton.svelte Component

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>

Building the ResetAllButton.svelte Component

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>

Building the VisualFeedback.svelte Component

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>

Building the KeyboardInput.svelte Component

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.

Assembling the App

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.

Styling the App

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.

Conclusion

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.

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment