<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>0xNelli</title>
        <link>https://paragraph.com/@0xnelli</link>
        <description>Fledging Security Researcher | Software Engineer | Onchain Enthusiast</description>
        <lastBuildDate>Fri, 19 Jun 2026 18:42:41 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>0xNelli</title>
            <url>https://storage.googleapis.com/papyrus_images/10d6441021773b83a21f44cf16004e1be5e2f944d58a82e4253a97e258b63b08.png</url>
            <link>https://paragraph.com/@0xnelli</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[ERC-165 Primer]]></title>
            <link>https://paragraph.com/@0xnelli/erc-165-primer</link>
            <guid>GcJwt4XW4VoOiHKeBJRL</guid>
            <pubDate>Thu, 14 Nov 2024 06:05:15 GMT</pubDate>
            <description><![CDATA[OverviewERC-165 is an interface identification mechanism that enhances compossibility as contracts can determine whether a contract supports an interface at compile time.ERC-165 GoalPrior to ERC-165, there was no mechanism to check whether a contract supported a specific interface, such as ERC-20. Therefore, there was no way of verifying whether a contract could accept tokens or tokens sent to a contract would be ‘stuck’ and effectively lost.ImplementationERC-165 introduces a standardized fun...]]></description>
            <content:encoded><![CDATA[<h2 id="h-overview" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Overview</h2><p>ERC-165 is an interface identification mechanism that enhances compossibility as contracts can determine whether a contract supports an interface at compile time.</p><h2 id="h-erc-165-goal" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">ERC-165 Goal</h2><p>Prior to ERC-165, there was no mechanism to check whether a contract supported a specific interface, such as ERC-20. Therefore, there was no way of verifying whether a contract could accept tokens or tokens sent to a contract would be ‘stuck’ and effectively lost.</p><h2 id="h-implementation" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Implementation</h2><p>ERC-165 introduces a standardized function to declare support for specific interfaces within a given contract. Other contracts can call this function to verify whether a certain interface is supported.</p><pre data-type="codeBlock" text="function supportsInteface(bytes4 interfaceId) external view returns (bool);
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">supportsInteface</span>(<span class="hljs-params"><span class="hljs-keyword">bytes4</span> interfaceId</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)</span>;
</code></pre><h3 id="h-supporting-multiple-interfaces" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Supporting multiple Interfaces</h3><p>In the wild you may have observed something like this:</p><pre data-type="codeBlock" text="function supportsInterface(bytes4 interfaceId) public view    override(ERC1155, AccessControl) returns (bool) { 
    return super.supportsInterface(interfaceId); 
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">supportsInterface</span>(<span class="hljs-params"><span class="hljs-keyword">bytes4</span> interfaceId</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span>    <span class="hljs-title"><span class="hljs-keyword">override</span></span>(<span class="hljs-params">ERC1155, AccessControl</span>) <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{ 
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">super</span>.supportsInterface(interfaceId); 
}
</code></pre><p>This is required when a contract inherits multiple instances of <code>supportsInterface</code> for different interface standards. In the example above, the contract inherits from both ERC-1155 and Access Control. The contract must override <code>supportsInterface</code> function and uses the <code>super</code> keyword to specify that the contract&apos;s own <code>supportsInteface</code> function should combine the from both interfaces. This way the <code>supportsInterface</code> function will return <code>true</code> for both queries with ERC-1155 and Access Control.</p><h2 id="h-sources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Sources</h2><ol><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-165">https://eips.ethereum.org/EIPS/eip-165</a></p></li></ol>]]></content:encoded>
            <author>0xnelli@newsletter.paragraph.com (0xNelli)</author>
        </item>
        <item>
            <title><![CDATA[Transparent Proxies & Upgradeability]]></title>
            <link>https://paragraph.com/@0xnelli/transparent-proxies-upgradeability</link>
            <guid>kusmkMcdxWgQdQsshnjo</guid>
            <pubDate>Thu, 14 Nov 2024 05:55:35 GMT</pubDate>
            <description><![CDATA[The Goal of the Proxy PatternSmart contracts are immutable, therefore it is not possible to change the logic of a smart contract once it has been deployed. However, the ability to update code is often vital, whether to add new features or to patch critical bugs and defects. This is what the proxy pattern offers to developers, a means of updating code by introducing upgradeability to smart contracts. Upgradeability is achieved by separating storage and logic. A proxy contract contains the stat...]]></description>
            <content:encoded><![CDATA[<h2 id="h-the-goal-of-the-proxy-pattern" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Goal of the Proxy Pattern</h2><p>Smart contracts are immutable, therefore it is not possible to change the logic of a smart contract once it has been deployed. However, the ability to update code is often vital, whether to add new features or to patch critical bugs and defects. This is what the proxy pattern offers to developers, a means of updating code by introducing upgradeability to smart contracts.</p><p>Upgradeability is achieved by separating storage and logic. A proxy contract contains the state and delegates execution to a logic contract. There is an opcode specifically for this called <code>delagatecall</code>. <code>delegatecall</code>, executes the code of a target address with the context of the caller. Meaning the state of the caller is used with the logic of the target.</p><p>To upgrade the logic, the proxy can be updated to reference a different logic contract with updated logic.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ea280c183dddd16b078f3fb718752beba6b6333da92633d7ed4f1fe3040f7361.png" alt="Proxy pattern flow (Credit: OpenZeppelin)" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Proxy pattern flow (Credit: OpenZeppelin)</figcaption></figure><h2 id="h-the-problems-with-proxies" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Problems with Proxies</h2><p>Conceptually the proxy setup is quite simple to grasp, however, it introduces a host of problems if not careful. A key issue that arises as a result of this pattern is clashes. This occurs for both the variables and function signatures of the proxy and logic contracts.</p><h3 id="h-variable-clashes" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Variable clashes</h3><h4 id="h-updating-the-incorrect-state-variable" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Updating the incorrect state variable</h4><p>This example is taken from a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.rareskills.io/post/delegatecall">RareSkills article</a> on <code>delegatecall</code>, please check it and RareSkills out.</p><p>Given the following contracts <code>Proxy</code> and <code>Logic</code> unintended behaviour arises as a result of variable clashes.</p><pre data-type="codeBlock" text="contract Logic {
    uint public number;

    function increment() public {
        number++;
    }
}

contract Proxy {
    address public calledAddress = 0xd9145CCE52D386f254917e481eB44e9943F39138;

    uint public myNumber;

    function callIncrement() public {        
        calledAddress.delegatecall(
            abi.encodeWithSignature(&quot;increment()&quot;)
        );
    }
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Logic</span> </span>{
    <span class="hljs-keyword">uint</span> <span class="hljs-keyword">public</span> number;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">increment</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        number<span class="hljs-operator">+</span><span class="hljs-operator">+</span>;
    }
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Proxy</span> </span>{
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> calledAddress <span class="hljs-operator">=</span> <span class="hljs-number">0xd9145CCE52D386f254917e481eB44e9943F39138</span>;

    <span class="hljs-keyword">uint</span> <span class="hljs-keyword">public</span> myNumber;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">callIncrement</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{        
        calledAddress.<span class="hljs-built_in">delegatecall</span>(
            <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSignature</span>(<span class="hljs-string">"increment()"</span>)
        );
    }
}
</code></pre><p>The <code>Proxy::callIncrement</code> function will call the <code>Logic::increment</code> function (shocking I know) with the context of the <code>Proxy</code> contract. The way the <code>increment</code> function should be interpreted is: to increment the value of storage slot zero of the contract. As stated in the article &quot;<strong>the name of the variable doesn’t matter; what is fundamental is which slot it is in.</strong>&quot;</p><p>Remember this logic was executed in the context of the Proxy contract. If storage slot zero is to be incremented by one, the <code>calledAddress</code> variable will then be updated to <code>0xd9145CCE52D386f254917e481eB44e9943F39139</code>. This breaks the Proxy contract as it is no longer able to delegate to the Logic contract. It is imperative to ensure there are no collisions between the slots of the Proxy and Logic contract as it can lead to unexpected behaviour breaking the smart contract.</p><h4 id="h-reading-from-the-incorrect-state-variable" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Reading from the incorrect state variable</h4><p>Again referencing an example from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.rareskills.io/post/delegatecall">RareSkills</a>, the following set of contracts have a crucial flaw due to variable clashes between <code>Proxy</code> and <code>Logic</code> contracts.</p><pre data-type="codeBlock" text="contract Logic {
    uint public discountRate = 20;

    function calculateDiscountPrice(uint256 amount) public pure returns (uint) {
        return amount - (amount * discountRate)/100;
    }
}

contract Proxy {
    uint public price = 200;
    address public called;

    function setCalled(address _called) public {
        called = _called;
    }

    function setDiscount() public  {
        (bool success, bytes memory data) = called.delegatecall(
            abi.encodeWithSignature(
                &quot;calculateDiscountPrice(uint256)&quot;, 
                price
            )
        );

        if (success) {
            uint newPrice = abi.decode(data, (uint256));
            price = newPrice;
        }
    }
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Logic</span> </span>{
    <span class="hljs-keyword">uint</span> <span class="hljs-keyword">public</span> discountRate <span class="hljs-operator">=</span> <span class="hljs-number">20</span>;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateDiscountPrice</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint</span></span>) </span>{
        <span class="hljs-keyword">return</span> amount <span class="hljs-operator">-</span> (amount <span class="hljs-operator">*</span> discountRate)<span class="hljs-operator">/</span><span class="hljs-number">100</span>;
    }
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Proxy</span> </span>{
    <span class="hljs-keyword">uint</span> <span class="hljs-keyword">public</span> price <span class="hljs-operator">=</span> <span class="hljs-number">200</span>;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> called;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setCalled</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _called</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        called <span class="hljs-operator">=</span> _called;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setDiscount</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span>  </span>{
        (<span class="hljs-keyword">bool</span> success, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> data) <span class="hljs-operator">=</span> called.<span class="hljs-built_in">delegatecall</span>(
            <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSignature</span>(
                <span class="hljs-string">"calculateDiscountPrice(uint256)"</span>, 
                price
            )
        );

        <span class="hljs-keyword">if</span> (success) {
            <span class="hljs-keyword">uint</span> newPrice <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(data, (<span class="hljs-keyword">uint256</span>));
            price <span class="hljs-operator">=</span> newPrice;
        }
    }
}
</code></pre><p>The <code>Logic::calculateDiscountPrice</code> function reads the state variable <code>discountRate</code> to calculate the new discounted price. However, this function is executed within the context of the <code>Proxy</code> contract. What the <code>Logic::calculateDiscountPrice</code> function is really doing is multiplying the parameter <code>amount</code> by the value in storage slot 0, dividing it by 100 and subtracting this from the original <code>amount</code> value. The <code>discountRate</code> is supposed to be 20, however, when executing within the context of the <code>Proxy</code> it is 200, as that is the value in storage slot 0. This leads to the state variable <code>price</code> being updated to the wrong value.</p><h3 id="h-function-signature-clashes" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Function signature clashes</h3><p>In the EVM each function is identified by the first 4-bytes of its Keccak-256 hash. An issue can arise in the Proxy pattern where different functions in the logic and proxy contracts both share the same signature.</p><p>The example below is taken from an <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://forum.openzeppelin.com/t/beware-of-the-proxy-learn-how-to-exploit-function-clashing/1070">OpenZeppelin blogpost</a>. It can be observed that the hash of <code>collate_propagate_storage(bytes16)</code> is the same as <code>burn(uint256)</code>.</p><pre data-type="codeBlock" text="pragma solidity ^0.5.0;

contract Proxy {
    
    address public proxyOwner;
    address public implementation;

    constructor(address implementation) public {
        proxyOwner = msg.sender;
        _setImplementation(implementation);
    }

    modifier onlyProxyOwner() {
        require(msg.sender == proxyOwner);
        _;
    }

    function upgrade(address implementation) external onlyProxyOwner {
        _setImplementation(implementation);
    }

    function _setImplementation(address imp) private {
        implementation = imp;
    }

    function () payable external {
        address impl = implementation;

        assembly {
            calldatacopy(0, 0, calldatasize)
            let result := delegatecall(gas, impl, 0, calldatasize, 0, 0)
            returndatacopy(0, 0, returndatasize)

            switch result
            case 0 { revert(0, returndatasize) }
            default { return(0, returndatasize) }
        }
    }
    
    // This is the function we&apos;re adding now
    function collate_propagate_storage(bytes16) external {
        implementation.delegatecall(abi.encodeWithSignature(
            &quot;transfer(address,uint256)&quot;, proxyOwner, 1000
        ));
    }
}
"><code><span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.5.0;</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Proxy</span> </span>{
    
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> proxyOwner;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> implementation;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> implementation</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        proxyOwner <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        _setImplementation(implementation);
    }

    <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">onlyProxyOwner</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> proxyOwner);
        <span class="hljs-keyword">_</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">upgrade</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> implementation</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyProxyOwner</span> </span>{
        _setImplementation(implementation);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_setImplementation</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> imp</span>) <span class="hljs-title"><span class="hljs-keyword">private</span></span> </span>{
        implementation <span class="hljs-operator">=</span> imp;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">payable</span></span> <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-keyword">address</span> impl <span class="hljs-operator">=</span> implementation;

        <span class="hljs-keyword">assembly</span> {
            <span class="hljs-built_in">calldatacopy</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">calldatasize</span>)
            <span class="hljs-keyword">let</span> result <span class="hljs-operator">:=</span> <span class="hljs-built_in">delegatecall</span>(<span class="hljs-built_in">gas</span>, impl, <span class="hljs-number">0</span>, <span class="hljs-built_in">calldatasize</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>)
            <span class="hljs-built_in">returndatacopy</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">returndatasize</span>)

            <span class="hljs-keyword">switch</span> result
            <span class="hljs-keyword">case</span> <span class="hljs-number">0</span> { <span class="hljs-keyword">revert</span>(<span class="hljs-number">0</span>, <span class="hljs-built_in">returndatasize</span>) }
            <span class="hljs-keyword">default</span> { <span class="hljs-keyword">return</span>(<span class="hljs-number">0</span>, <span class="hljs-built_in">returndatasize</span>) }
        }
    }
    
    <span class="hljs-comment">// This is the function we're adding now</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">collate_propagate_storage</span>(<span class="hljs-params"><span class="hljs-keyword">bytes16</span></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        implementation.<span class="hljs-built_in">delegatecall</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSignature</span>(
            <span class="hljs-string">"transfer(address,uint256)"</span>, proxyOwner, <span class="hljs-number">1000</span>
        ));
    }
}
</code></pre><p>Therefore if a user calls the <code>Proxy</code> to burn some tokens it will match the signature of <code>collate_propagate_storage(bytes16)</code> and send 1000 tokens to the owner of the proxy instead.</p><p>Note that this cannot occur in a singular contract, as the compiler will identify a clash of signatures and throw an error.</p><h3 id="h-constructor-caveats" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Constructor Caveats</h3><p>Code in the constructor of a logic contract is effectively useless as the code only runs once during its deployment and any resulting state changes are irrelevant as the context of execution comes from the Proxy contract. As said in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies">proxies blog</a> on OpenZeppelin: &quot;Proxies are completely oblivious to the storage trie changes that are performed by the constructor.&quot;</p><p>Therefore, in any logic contract, if there is any initial setup required, an <code>initializer</code> function should be used. OpenZeppelin supports this with its host of <code>upgradeable</code> contracts.</p><h2 id="h-transparent-proxies" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Transparent Proxies</h2><p>Transparent proxies is a standard of implementing upgradeability via a Proxy contract, that avoids the pitfalls previously explored.</p><p>Transparent proxies have message-routing logic based on the <code>msg.sender</code>. If the caller is the <code>admin</code> of the Proxy contract, the proxy will not delegate calls. If the caller is not the <code>admin</code> the proxy will always delegate calls. This behaviour is demonstrated in the table below:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/85d7c094913f5c8371c6d4ca5477b332368e998ea00769e07090b37551bc5e0c.png" alt="msg.sender routing" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">msg.sender routing</figcaption></figure><h3 id="h-erc-1967" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">ERC-1967</h3><p>Every proxy must store the address of the logic contract, however as previously explored there is a danger of storage variable clashes altering this value. To ensure that a clash never occurs for the address of the logic function, ERC-1967 implemented a reserved slot for storing the address of a logic contract. This ensures that the compiler will never assign a variable to this slot, preventing clashes and unexpected behaviour from occurring. All proxy contracts from OpenZeppelin implement ERC-1967.</p><h2 id="h-sources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Sources</h2><ol><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.openzeppelin.com/the-transparent-proxy-pattern">https://blog.openzeppelin.com/the-transparent-proxy-pattern</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts/5.x/api/proxy#Proxy">https://docs.openzeppelin.com/contracts/5.x/api/proxy#Proxy</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies">https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-1967">https://eips.ethereum.org/EIPS/eip-1967</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.rareskills.io/post/delegatecall">https://www.rareskills.io/post/delegatecall</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://forum.openzeppelin.com/t/beware-of-the-proxy-learn-how-to-exploit-function-clashing/1070">https://forum.openzeppelin.com/t/beware-of-the-proxy-learn-how-to-exploit-function-clashing/1070</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/nomic-foundation-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357">https://medium.com/nomic-foundation-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357</a></p></li></ol>]]></content:encoded>
            <author>0xnelli@newsletter.paragraph.com (0xNelli)</author>
        </item>
        <item>
            <title><![CDATA[ERC-1155 Primer]]></title>
            <link>https://paragraph.com/@0xnelli/erc-1155-primer</link>
            <guid>zhgQXXKhktWLFchN0PN4</guid>
            <pubDate>Thu, 14 Nov 2024 00:17:32 GMT</pubDate>
            <description><![CDATA[OverviewThe ERC-1155 standard specifies an interface that supports multiple tokens governed by a single contract. Additionally, tokens controlled by the contract can have different types of fungibility. Supported fungibility types include:Fungible (ERC-20 like)Semi-fungibleNon-fungible (ERC-721 like)It also introduces a number of QoL features, such as bulk approvals, where one approval applies to all token types held by the contract. This removes the need for users to approve an individual to...]]></description>
            <content:encoded><![CDATA[<h2 id="h-overview" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Overview</h2><p>The ERC-1155 standard specifies an interface that supports multiple tokens governed by a single contract. Additionally, tokens controlled by the contract can have different types of fungibility.</p><p>Supported fungibility types include:</p><ul><li><p>Fungible (ERC-20 like)</p></li><li><p>Semi-fungible</p></li><li><p>Non-fungible (ERC-721 like)</p></li></ul><p>It also introduces a number of QoL features, such as bulk approvals, where one approval applies to all token types held by the contract. This removes the need for users to approve an individual token each time they transact with it. Moreover, it introduces batched transactions. As the contract supports multiple tokens, it provides a mechanism to query balances for multiple tokens in one transaction or to transfer multiple token types in one transaction. Finally, it also implements safe transfers to ensure that the receiving entity supports the ERC-1155 token type.</p><h2 id="h-semi-fungibility" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Semi Fungibility</h2><p>This fungibility type refers to a token that possesses unique metadata and properties but can be held by multiple addresses. For example, a game may require a key to gain access to a specific level or hidden content. The token representing the key is unique and would have metadata and properties related to the content it unlocks. However, many users of the game may hold the token.</p><p>This type of token has characteristics of both an ERC-20 and ERC-721. It represents a unique asset with its own properties and metadata, like an ERC-721. However, these tokens have multiple owners and are interchangeable with one another, as they represent the same asset, much like an ERC-20.</p><h2 id="h-batch-operations" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Batch Operations</h2><p>Batch operations introduce significant gas savings as they permit multiple balance queries or transfers to be completed in a single transaction. This is only possible as the ERC-1155 standard maintains the state of multiple tokens in a single contract.</p><p><strong>Batch Query Balance:</strong></p><p>Queries an array of addresses (<code>accounts</code>) for the balance they hold of the token type <code>id</code>. Returns an array of token balances in the order of the <code>accounts</code> provided to the function call.</p><pre data-type="codeBlock" text="balanceOfBatch(address[] accounts, uint256[] ids) -&gt; uint256[]
"><code>balanceOfBatch(<span class="hljs-keyword">address</span>[] accounts, <span class="hljs-keyword">uint256</span>[] ids) <span class="hljs-operator">-</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>[]
</code></pre><ul><li><p>The length of <code>accounts</code> must equal the length of <code>ids</code></p></li><li><p>You can query multiple addresses for the balance of different tokens the contract holds</p><ul><li><p>E.g. query two addresses the balance of gold(fungible), potions(semi-fungible) and unique swords(non-fungible) the each own all in one transaction</p></li></ul></li></ul><p><strong>Batch Transfer:</strong></p><p>You can transfer multiple different types of tokens from one address to another in the same transaction.</p><pre data-type="codeBlock" text="safeBatchTransferFrom(address from, address to, uint256[] ids, uint256[] amounts, bytes data);
"><code>safeBatchTransferFrom(<span class="hljs-selector-tag">address</span> <span class="hljs-selector-tag">from</span>, <span class="hljs-selector-tag">address</span> <span class="hljs-selector-tag">to</span>, uint256<span class="hljs-selector-attr">[]</span> ids, uint256<span class="hljs-selector-attr">[]</span> amounts, bytes data);
</code></pre><ul><li><p><code>ids</code> and <code>amounts</code> must have the same length</p></li><li><p><code>to</code> must implement <code>onERC1155BatchReceived</code></p></li></ul><h3 id="h-openzeppelin-extended-batch-functionality" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">OpenZeppelin extended batch functionality:</h3><p>OpenZeppelin extended the batch features included in the ERC-1155 standard, introducing a <code>mintBatch</code> and a <code>burnBatch</code>.</p><p><strong>Mint Batch:</strong></p><p>Mint multiple tokens in one transaction. The mint can also include multiple types of tokens, e.g. mint ERC-20 and ERC-721 tokens to the same address in a single transaction.</p><pre data-type="codeBlock" text="_mintBatch(address to, uint256[] ids, uint256[] values, bytes data);
"><code>_mintBatch(<span class="hljs-selector-tag">address</span> <span class="hljs-selector-tag">to</span>, uint256<span class="hljs-selector-attr">[]</span> ids, uint256<span class="hljs-selector-attr">[]</span> values, bytes data);
</code></pre><ul><li><p><code>ids</code> and <code>values</code> must have the same length</p></li><li><p><code>to</code> cannot be the zero address</p></li><li><p>The <code>to</code> address must implement <code>onERC1155BatchReceived</code></p></li></ul><p><strong>Burn Batch:</strong></p><p>Burn multiple tokens in a single transaction.</p><pre data-type="codeBlock" text="_burnBatch(address from, uint256[] ids, uint256[] values);
"><code>_burnBatch(<span class="hljs-selector-tag">address</span> <span class="hljs-selector-tag">from</span>, uint256<span class="hljs-selector-attr">[]</span> ids, uint256<span class="hljs-selector-attr">[]</span> values);
</code></pre><ul><li><p><code>from</code> cannot be the zero address</p></li><li><p><code>from</code> must have at least the amount of tokens specified in the <code>values</code> parameter of the token represented by the array position</p></li><li><p><code>ids</code> and <code>values</code> must have the same length</p></li></ul><h3 id="h-supply-extension" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Supply Extension</h3><p>This is an additional extension that can be used to track the total supply of each of the tokens in the ERC-1155 contract. The only balance related functionality enshrined in the ERC-1155 standard is the <code>balanceOf</code> and <code>balanceOfBatch</code> functions, that only reveal information about the tokens an address holds.</p><p>This extension adds the following functions:</p><ul><li><p><code>totalSupply(id)</code> which returns the total amount of tokens with the id <code>id</code>.</p></li><li><p><code>totalSupply()</code> which returns the number of tokens the ERC1155 contract holds.</p></li><li><p><code>exists(id)</code> returns a <code>bool</code> as to whether the token of <code>id</code> exists</p></li></ul><h2 id="h-sources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Sources</h2><ol><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-1155">https://eips.ethereum.org/EIPS/eip-1155</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ethereum.org/en/developers/docs/standards/tokens/erc-1155/">https://ethereum.org/en/developers/docs/standards/tokens/erc-1155/</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts/5.x/erc1155">https://docs.openzeppelin.com/contracts/5.x/erc1155</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts/5.x/api/token/erc1155#ERC1155">https://docs.openzeppelin.com/contracts/5.x/api/token/erc1155#ERC1155</a></p></li></ol>]]></content:encoded>
            <author>0xnelli@newsletter.paragraph.com (0xNelli)</author>
        </item>
        <item>
            <title><![CDATA[I built an Onchain App]]></title>
            <link>https://paragraph.com/@0xnelli/i-built-an-onchain-app</link>
            <guid>4ZQTeX8cYvEdt6M1sqSU</guid>
            <pubDate>Thu, 07 Nov 2024 12:39:10 GMT</pubDate>
            <description><![CDATA[MotivationAs a long-time onchain enthusiast who has been dabbling in DeFi and battling with Metamask since 2020; I have always marvelled at the premier onchain apps. Apps like AAVE and UniSwap are so clean and always iterating and improving. Furthermore and perhaps most importantly, in the most adversarial environment imaginable with billions of dollars at stake, these apps have never been exploited. *I hope I didn’t just jinx that😬🤞* But since I have started using these applications, I hav...]]></description>
            <content:encoded><![CDATA[<h2 id="h-motivation" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Motivation</h2><p>As a long-time onchain enthusiast who has been dabbling in DeFi and battling with Metamask since 2020; I have always marvelled at the premier onchain apps. Apps like AAVE and UniSwap are so clean and always iterating and improving. Furthermore and perhaps most importantly, in the most adversarial environment imaginable with billions of dollars at stake, these apps have never been exploited.</p><p>*I hope I didn’t just jinx that<strong>😬</strong>🤞*</p><p>But since I have started using these applications, I have graduated from university as a software engineer and have a few years of experience under my belt, I no longer marvel at onchain apps. Now they pique my curiosity. What goes into creating an onchain app? What is the best way to build one? How much effort does it take to go from nothing to a URL that hosts a clean frontend that allows users to interact with a decentralised protocol? I needed to find out, so that’s why I built an onchain app.</p><h2 id="h-the-idea" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Idea</h2><p>Ultimately the idea doesn’t matter too much here. The purpose of this project is to understand how to build the thing, rather than building the thing itself. I know, I know if you are passionate about the application you are building, the odds of you completing it dramatically increase. But you know what, I am passionate about learning so that’ll suffice.</p><p>So what am I building?</p><p>Anyone who has attempted to develop anything onchain would have undoubtedly come across the man himself <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/PatrickAlphaC">Patrick Collins</a>. The content he has created is at the pinnacle of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://updraft.cyfrin.io/">onchain development and security</a>, I strongly recommend you check it out.</p><p>In one of his <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.youtube.com/watch?v=sas02qSFZ74&amp;list=PL4Rj_WH6yLgWe7TxankiqkrkVKXIwOP42&amp;index=2">courses</a>, the exercise is to build a set of smart contracts that facilitate decentralised fundraising. The project allows the owner of the contract to raise funds and withdraw them to presumably go out and do what they promised to do with the funds. However, in the onchain world, this isn’t always what happens with teams that raise big funds.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/dd33a90efb62f73d220c6a16450495fca3f48e70835fca3c959782eb97b8ebb1.jpg" alt="Web3 Founder Mode" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Web3 Founder Mode</figcaption></figure><p>Shenanigans aside, I thought I would extend the capabilities of this project to scale to multiple users being able to create different fund-raises that anyone with a Metamask can contribute to. To build a Kickstarter-esque onchain application.</p><p>And with that decision made, it became time to select the tech stack I would use to create my new app, <strong>Go Fund Yourself.</strong></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/400c1008f9fb6113a6c0c588455fd6a677e1fe1495bd584914102f76ccbfeb10.webp" alt="Yes I stole the name from South Park..." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Yes I stole the name from South Park...</figcaption></figure><h2 id="h-choosing-the-tech-stack" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Choosing the tech stack</h2><h3 id="h-frontend" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Frontend</h3><p>In this day and age, there is certainly no shortage of frontend frameworks to choose from. I personally use VueJs at work, so I ruled it out as I wanted to try something new. Based on the docs for different onchain libraries (more on this later), NextJs and React seem to be the most popular. While these frameworks will undoubtedly boost your employability, the contrarian within me wouldn’t allow it. Also as someone who uses VueJs regularly, I really do enjoy the templating in a vue component as opposed to JSX, so that played a factor too.</p><p>I ultimately opted for SvelteKit. I get templating syntax which is my preference. Additionally, I wanted to try a meta-framework to better understand their strengths and weaknesses and familiarise myself with the server and client-side separations when developing components. Moreover, Svelte is consistently voted as one of the most admired frontend frameworks by developer surveys and I’m a big fan of Svelte’s philosophy:</p><blockquote><p>“Svelte is a superset of HTML” - Rich Harris (Svelte Founder)</p></blockquote><p>Some front-end frameworks stray far from the idiomatic HTML, JavaScript &amp; CSS that I first used to create simple websites. It was with these languages that I first fell in love with coding. I wanted to see if Svelte made good on its promise of simplicity and idiomatic use of the aforementioned languages and could emulate that feeling of when I first started building websites.</p><p>For more info on Svelte, I strongly recommend checking out RIch’s interviews with the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.youtube.com/watch?v=4tBYVFXSJbo&amp;t=2862s">Primeagean</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.youtube.com/watch?v=z7n17ajJpCo">Prismic</a>. Additionally, the man himself Patrick Collins made a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.youtube.com/watch?v=f_nkq_w4eOU&amp;t=13759s">stream</a> for web3 front-end development and he was using SvelteKit, I saw this as a good omen, how could I not give Svelte a crack?</p><h3 id="h-onchain-libraries" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Onchain Libraries</h3><p>You might be thinking, what on Earth is an onchain library? These are JavaScript libraries that provide utilities for all types of interactions with blockchains. These libraries greatly simplify the process of reading/writing from/to smart contracts, managing wallet connections and updating the state of your application accordingly.</p><p>As far as I am aware, there are three major EVM libraries to consider:</p><ol><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.web3js.org/">Web3.js</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.ethers.org/v5/getting-started/">Ethers.js</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://wagmi.sh/core/getting-started">Wagmi</a></p></li></ol><p>Each has its pros and cons. There’s a fantastic article on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://sveltekit.io/blog/web3-svelte">Sveltekit.io</a> that breaks down the different library alternatives when creating an onchain SvelteKit app. Strongly recommend you check it out.</p><p>But I’ll break down my decision for you here quickly:</p><ul><li><p>Web3.js is the most mature of the three and offers a rich set of features. However, it is quite a large bundle and is not quite as performant as the other options.</p></li><li><p>Ethers.js I have worked with in the past and found the DX to be okay, it is more performant than Web3.js, but lacks certain utilities that are offered by Wagmi.</p></li><li><p>Wagmi has both good performance and a rich set of utilities. Furthermore, it is rapidly gaining in popularity and is used by some of the biggest projects in the onchain world.</p></li></ul><p>I ultimately chose Wagmi. It’s important to note when using Svelte it will be the Wagmi core JS library that will power onchain interactions. There are specific libraries for React and Vue that offer hooks or composables, but these of course are of no use to us. However, there is an unofficial <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/softwarecurator/svelte-wagmi">SvelteKit-specific library</a> that has received high praise from Svelte developers. It offers Wagmi stores that simplify integrating Wagmi into your Svelte app, which I did take advantage of.</p><h4 id="h-rpcs" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">RPCs</h4><p>If you are developing anything that is connected to a blockchain you will need a RPC (Remote Procedure Call). At a high level, RPC is a protocol that allows a client (you or your app) to perform operations on a server (the blockchain).</p><p>There are two major service providers that I am aware of for this: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.infura.io/api">Infura</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.alchemy.com/reference/ethereum-api-quickstart">Alchemy</a>. I have used Alchemy previously and reused an RPC that I created previously, but I urge you to investigate both and choose the best option for you.</p><h3 id="h-backend" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Backend</h3><p>The backend for this app is the blockchain, obviously! But which one?</p><p>The first decision is whether to deploy the contracts to a testnet or mainnet. The former does not use real assets of value whereas the latter does. I decided for my first onchain app to deploy to a testnet while learning the ropes, ensuring my own or any potential user funds are not in jeopardy. After all, this is a learning exercise.</p><p>Next decision which testnet? For this I decided to stick with Ethereum Sepolia, as it has all the integrations with major service providers you can ask for, like Alchemy and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chain.link/">Chainlink</a>. Also procuring Sepolia testnet eth to make transactions is pretty straightforward as there are plenty of faucets available.</p><p>Finally, I decided that I did not want all the data this app collects to be stored onchain. I used an SQLite database to store the descriptions of why each user is raising funds for their fund-raise. Storing this information on a db saves the users on gas fees. Additionally, I wanted the experience of working with SvelteKit and SQLite. I wanted to learn the pitfalls and proper patterns involved in CRUD operations with a frontend meta-framework and a db.</p><h3 id="h-devops" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">DevOps</h3><blockquote><p>“Work incredibly hard in order to be incredibly lazy“ - Patick Collins</p></blockquote><p>I absolutely love this quote from Patrick. It captures the essence and ethos of software development so well. As a software engineer, you want to work exceedingly hard to automate your workflow as much as possible, so you can be lazy when developing. ‘Lazy’ meaning you can focus on only delivering value; as the tedious work that needs to be done around shipping is taken care of for you.</p><p>How does this translate to the Go Fund Yourself app?</p><p>I would like to set up the project so that any changes I push to the main branch of my repo are automatically deployed into production and accessible to users nearly immediately after I make these changes. This pertains to the changes made to the frontend, not the smart contracts which are immutable.</p><p>This raises the question of how am I hosting my frontend? After listening to the Lex Fridman podcast with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.youtube.com/watch?v=oFtjKbXKqbg&amp;t=1s">Peter Levels</a>, I was inspired to learn how to host my own applications on a VPS. There are a plethora of services that simplify the deployment and hosting process, however, according to this podcast it seems that Peter runs an entire empire using some VPS’ and Nginx. Furthermore, the hallmark of a great software engineer is the ability to rawdog it rather than using the latest abstracted solution.</p><p>So the DevOps requirement of this project is to spin up a VPS, set it up to run my SvelteKit application and then create a pipeline to allow the latest changes I have made to automatically be detected by the server, pulled in and then run so that they are accessible to the end users.</p><p>To achieve this I chose <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://hpanel.hostinger.com/servers">Hostinger</a> for the VPS, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy">Nginx</a> for a reverse proxy, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://certbot.eff.org/pages/about">Certbot</a> for SSL certificates to enable https and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.github.com/en/actions">GitHub actions</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.docker.com/get-started/">Docker</a> to create the pipeline.</p><h2 id="h-wrapping-up" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Wrapping up</h2><p>The purpose of this article was to explain the motivation for building Go Fund Yourself and the reasoning that went into selecting the different technologies in the tech stack.</p><p>While this started as an exercise to understand the process of onchain app development, I admittedly increased the scope of the project to include aspects of Web2 development that I haven’t had much experience in yet. I could have stored all the information about fund-raises on the blockchain, however, I chose to include an SQLite db, because I have never worked with this database before. Additionally, I wanted to understand the process of using a front-end meta-framework with a db. For deployment, I could have used <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://vercel.com/">Vercel</a> or another similar service, however, I wanted to understand what it took to configure your own server and pipelines to better comprehend why these abstracted solutions have gained so much popularity. Is setting up your own server worth the heartache or should you just use Vercel? What are the tradeoffs? These are the questions I was seeking to answer.</p><p>This is just the first article in a series I am working on. Now that you know my thought process, I look forward to sharing with you how I built Go Fund Yourself. In this series, I will explore the pitfalls and learnings of each stage of the development process. I hope you can use this series to gain a better understanding of onchain development, as a blueprint to build your own onchain app or even as a tutorial to add to your portfolio on GitHub.</p><p>Make sure you subscribe to ensure you do not miss new instalments of this series:</p><div data-type="subscribeButton" class="center-contents"><a class="email-subscribe-button" href="null">Subscribe</a></div><p>And please feel free to mint to show support:</p><p>Look forward to seeing you in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0x194541D1009d22f7aE586c3AAeF4273B5aA79596/-0HZmBqg9iOs8j2DMCtMQNdebDscg3SuBvoPfZmU098">next article</a> where I delve into SvelteKit and front-end development. 👋</p>]]></content:encoded>
            <author>0xnelli@newsletter.paragraph.com (0xNelli)</author>
        </item>
        <item>
            <title><![CDATA[I built an Onchain App: Front-end - Part 1]]></title>
            <link>https://paragraph.com/@0xnelli/i-built-an-onchain-app-front-end-part-1</link>
            <guid>9ZU4yCxH60SGMHiFl0Yi</guid>
            <pubDate>Thu, 07 Nov 2024 12:26:34 GMT</pubDate>
            <description><![CDATA[The code for the first stage of development explored in this article can be found here. Please use this to provide additional context in the event that any parts of this article are unclear. Additionally, if aspects of this article are unclear or do not follow best practices please let me on 𝕏.Project SetupSvelteKit InitialisationAs aforementioned in the previous article, I am using SvelteKit to build out my frontend. It is important to note that while Svelte recently announced the release o...]]></description>
            <content:encoded><![CDATA[<p><em>The code for the first stage of development explored in this article can be found </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/MGM103/svelte5-onchain-app/tree/main/frontend-part-1"><em>here</em></a><em>. Please use this to provide additional context in the event that any parts of this article are unclear. Additionally, if aspects of this article are unclear or do not follow best practices please let me on </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/0xNelli"><em>𝕏</em></a><em>.</em></p><h2 id="h-project-setup" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Project Setup</h2><h3 id="h-sveltekit-initialisation" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">SvelteKit Initialisation</h3><p>As aforementioned in the previous article, I am using SvelteKit to build out my frontend. It is important to note that while Svelte recently announced the release of the latest version <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://svelte.dev/blog/svelte-5-is-alive">Svelte 5</a>, I made this project before that and everything you see was done using Svelte 4. Just giving you all the heads up that there will be no runes here. Luckily Svelte 4 is backwards compatible with Svelte 5, so you can use the latest version and reap all the new performance rewards without any consequences.</p><p>Step 1 of course is to create the project, which is easy enough using the CLI:</p><pre data-type="codeBlock" text="npx sv create go-fund-yourself-fe
cd go-fund-yourself-fe
npm install
npm run dev
"><code>npx sv create go<span class="hljs-operator">-</span>fund<span class="hljs-operator">-</span>yourself<span class="hljs-operator">-</span>fe
cd go<span class="hljs-operator">-</span>fund<span class="hljs-operator">-</span>yourself<span class="hljs-operator">-</span>fe
npm install
npm run dev
</code></pre><p><em>Please note that I am currently in the root directory of my previously created </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Cyfrin/foundry-fund-me-cu/tree/main"><em>fundme_foundry</em></a><em> project.</em></p><p>When prompted for initial setup options, I chose:</p><ol><li><p>Minimal template</p></li><li><p>no type checking</p></li><li><p>eslint &amp; prettier</p></li><li><p>npm</p></li></ol><h3 id="h-scss" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">SCSS</h3><p>Alrighty, so personally I am a sass preprocessor type of guy, as I prefer to use SCSS to style components. A preprocessor extends the capabilities of vanilla CSS and adds features such as variables, nesting, mixins and functions.</p><p>If the above appeals to you, simply navigate to your <code>svelte.config.js</code> file and make the following changes to your imports and config:</p><pre data-type="codeBlock" text="import adapter from &apos;@sveltejs/adapter-auto&apos;;
import { vitePreprocess } from &apos;@sveltejs/vite-plugin-svelte&apos;;

/** @type {import(&apos;@sveltejs/kit&apos;).Config} */
const config = {
  kit: {
    adapter: adapter()
  },
  preprocess: [vitePreprocess()]
};

export default config;
"><code><span class="hljs-keyword">import</span> adapter from <span class="hljs-string">'@sveltejs/adapter-auto'</span>;
<span class="hljs-keyword">import</span> { vitePreprocess } from <span class="hljs-string">'@sveltejs/vite-plugin-svelte'</span>;

<span class="hljs-comment">/** @type {import('@sveltejs/kit').Config} */</span>
<span class="hljs-type">const</span> config = {
  kit: {
    adapter: <span class="hljs-built_in">adapter</span>()
  },
  preprocess: [<span class="hljs-built_in">vitePreprocess</span>()]
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> config;
</code></pre><p>Additionally, in the <code>vite.config.js</code> file, the following changes are required with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://sass-lang.com/documentation/breaking-changes/legacy-js-api/">newer versions of sass</a>:</p><pre data-type="codeBlock" text="import { sveltekit } from &apos;@sveltejs/kit/vite&apos;;
import { defineConfig } from &apos;vite&apos;;

export default defineConfig({
  plugins: [sveltekit()],
  css: {
    preprocessorOptions: {
      scss: {
        api: &apos;modern-compiler&apos;
      }
    }
  }
});
"><code>import { sveltekit } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit/vite'</span>;
import { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'vite'</span>;

export <span class="hljs-keyword">default</span> <span class="hljs-title function_ invoke__">defineConfig</span>({
  <span class="hljs-attr">plugins</span>: [<span class="hljs-title function_ invoke__">sveltekit</span>()],
  <span class="hljs-attr">css</span>: {
    <span class="hljs-attr">preprocessorOptions</span>: {
      <span class="hljs-attr">scss</span>: {
        <span class="hljs-attr">api</span>: <span class="hljs-string">'modern-compiler'</span>
      }
    }
  }
});
</code></pre><h2 id="h-design-philosophy" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Design Philosophy</h2><p>Go Fund Yourself needs a homepage.</p><p>This sounds simple enough, but while creating Go Fund Yourself I discovered I have very little in the way of design skills. I usually rely on professional designers to create great-looking frontends in Figma and do my best to bring their vision to life. Without this crutch, designing a website was initially quite an intimidating proposition.</p><p>A few questions sprang to mind when attempting to develop a design. What colours should be used? What fonts should be chosen? Should animations be incorporated to make it feel more alive and modern? How can Go Fund Yourself create a good first impression with new users?</p><p>Let’s tackle these questions one at a time.</p><h3 id="h-colour-scheme" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Colour Scheme</h3><p>I stumbled onto a great YouTuber by the name of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.youtube.com/@whosajid">Sajid</a>. He has great practical design advice oriented towards devs. His video on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.youtube.com/watch?v=AmY3db_Qs94">random design tips</a> starts with a guide on selecting colour palettes for a website and he even provides a site to pick the 4 distinct <code>HSL</code> values for you <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.iamsajid.com/colors/">here</a>.</p><p>These are the colour values I selected for Go Fund Yourself:</p><pre data-type="codeBlock" text="// Light mode colours
$PRIMARY_LIGHT: hsl(220, 50%, 90%); 
$SECONDARY_LIGHT: hsl(220, 50%, 10%); 
$TERTIARY_LIGHT: hsl(280, 80%, 20%); 
$ACCENT_LIGHT: hsl(160, 80%, 20%);

// Dark mode colours
$PRIMARY_DARK: hsl(220, 50%, 10%); 
$SECONDARY_DARK: hsl(220, 50%, 90%); 
$TERTIARY_DARK: hsl(280, 80%, 80%); 
$ACCENT_DARK: hsl(160, 80%, 80%);
"><code><span class="hljs-comment">// Light mode colours</span>
$PRIMARY_LIGHT: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">90</span><span class="hljs-operator">%</span>); 
$SECONDARY_LIGHT: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">10</span><span class="hljs-operator">%</span>); 
$TERTIARY_LIGHT: hsl(<span class="hljs-number">280</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>, <span class="hljs-number">20</span><span class="hljs-operator">%</span>); 
$ACCENT_LIGHT: hsl(<span class="hljs-number">160</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>, <span class="hljs-number">20</span><span class="hljs-operator">%</span>);

<span class="hljs-comment">// Dark mode colours</span>
$PRIMARY_DARK: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">10</span><span class="hljs-operator">%</span>); 
$SECONDARY_DARK: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">90</span><span class="hljs-operator">%</span>); 
$TERTIARY_DARK: hsl(<span class="hljs-number">280</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>); 
$ACCENT_DARK: hsl(<span class="hljs-number">160</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>);
</code></pre><h3 id="h-surfaces" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Surfaces</h3><p>Exploring style libraries to gain a better idea of how to bring a professional-looking website alive, I encountered <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://open-props.style/#getting-started">Open Props</a>. Open props have the idea of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://codepen.io/argyleink/pen/XWaYyWe">surfaces</a>:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/777c5a416a69433199ef85cfbb983648bc05bfd4e100e2db23f47c1faff0d65b.png" alt="Open Props Surfaces" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Open Props Surfaces</figcaption></figure><p>This allows content to be layered in a manner that visually informs the user of related content groupings. Each of the surfaces seen in the image above also has a box shadow to give it a more tactile feel and make the layering more apparent. Therefore shadow colour values must also be incorporated into the design.</p><p>I opted to adapt this surface concept into the CSS of the app rather than installing Open Props itself. I do not want to stand on the shoulders of giants with this project, I want to create everything from scratch and that means rawdogging the CSS in this case.</p><pre data-type="codeBlock" text="// Light mode colours
$LIGHT-SURFACE-1: hsl(220, 50%, 90%); 
$LIGHT-SURFACE-2: hsl(220, 50%, 95%);
$LIGHT-SURFACE-3: hsl(220, 50%, 100%);
$LIGHT-SHADOW-1: hsla(220, 50%, 10%, 0.3);
$LIGHT-SHADOW-2: hsla(220, 50%, 10%, 0.15);
$SECONDARY-LIGHT: hsl(220, 50%, 10%); 
$TERTIARY-LIGHT: hsl(280, 80%, 20%); 
$ACCENT-LIGHT: hsl(160, 80%, 20%);

// Dark mode colours
$DARK-SURFACE-1: hsl(220, 50%, 10%); 
$DARK-SURFACE-2: hsl(220, 50%, 15%); 
$DARK-SURFACE-3: hsl(220, 50%, 20%); 
$DARK-SHADOW-1: hsla(220, 50%, 90%, 0.3);
$DARK-SHADOW-2: hsla(220, 50%, 90%, 0.15);
$SECONDARY-DARK: hsl(220, 50%, 90%); 
$TERTIARY-DARK: hsl(280, 80%, 80%); 
$ACCENT-DARK: hsl(160, 80%, 80%);
"><code><span class="hljs-comment">// Light mode colours</span>
$LIGHT<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-1</span>: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">90</span><span class="hljs-operator">%</span>); 
$LIGHT<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-2</span>: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">95</span><span class="hljs-operator">%</span>);
$LIGHT<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-3</span>: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">100</span><span class="hljs-operator">%</span>);
$LIGHT<span class="hljs-operator">-</span>SHADOW<span class="hljs-number">-1</span>: hsla(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">10</span><span class="hljs-operator">%</span>, <span class="hljs-number">0</span><span class="hljs-number">.3</span>);
$LIGHT<span class="hljs-operator">-</span>SHADOW<span class="hljs-number">-2</span>: hsla(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">10</span><span class="hljs-operator">%</span>, <span class="hljs-number">0</span><span class="hljs-number">.15</span>);
$SECONDARY<span class="hljs-operator">-</span>LIGHT: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">10</span><span class="hljs-operator">%</span>); 
$TERTIARY<span class="hljs-operator">-</span>LIGHT: hsl(<span class="hljs-number">280</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>, <span class="hljs-number">20</span><span class="hljs-operator">%</span>); 
$ACCENT<span class="hljs-operator">-</span>LIGHT: hsl(<span class="hljs-number">160</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>, <span class="hljs-number">20</span><span class="hljs-operator">%</span>);

<span class="hljs-comment">// Dark mode colours</span>
$DARK<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-1</span>: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">10</span><span class="hljs-operator">%</span>); 
$DARK<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-2</span>: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">15</span><span class="hljs-operator">%</span>); 
$DARK<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-3</span>: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">20</span><span class="hljs-operator">%</span>); 
$DARK<span class="hljs-operator">-</span>SHADOW<span class="hljs-number">-1</span>: hsla(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">90</span><span class="hljs-operator">%</span>, <span class="hljs-number">0</span><span class="hljs-number">.3</span>);
$DARK<span class="hljs-operator">-</span>SHADOW<span class="hljs-number">-2</span>: hsla(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">90</span><span class="hljs-operator">%</span>, <span class="hljs-number">0</span><span class="hljs-number">.15</span>);
$SECONDARY<span class="hljs-operator">-</span>DARK: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">90</span><span class="hljs-operator">%</span>); 
$TERTIARY<span class="hljs-operator">-</span>DARK: hsl(<span class="hljs-number">280</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>); 
$ACCENT<span class="hljs-operator">-</span>DARK: hsl(<span class="hljs-number">160</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>);
</code></pre><h3 id="h-fonts" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Fonts</h3><p>Previously, I used Google Fonts for all my font needs. To no one’s surprise, Google fonts is a font server provided by Google, with a plethora of fonts that can be added to your CSS.</p><p>However, recently I was made aware of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://fontsource.org/">Fontsource</a>. Font source allows you to self-host fonts in your project, this provides a boost to performance as your app no longer needs to communicate with the Google font server and increases privacy compliance by also removing this interaction. I chose the <code>Ubuntu sans-serif</code> font for Go Fund Yourself.</p><p>You can add these fonts via the CLI:</p><pre data-type="codeBlock" text="npm install @fontsource-variable/ubuntu-sans
"><code>npm install @fontsource<span class="hljs-operator">-</span>variable<span class="hljs-operator">/</span>ubuntu<span class="hljs-operator">-</span>sans
</code></pre><h3 id="h-main-style-sheet" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Main Style Sheet</h3><p>Okay, now that we’ve made some key design decisions it’s time to put them into action. We will start by creating a <code>main.scss</code> file within a new directory in the <code>lib</code> directory called <code>styles</code>.</p><p><em>If you are not using a preprocessor you should just create a</em> <code>.css</code> <em>file and adapt the content appropriately</em>.</p><pre data-type="codeBlock" text="// src/lib/styles/main.scss

@use &apos;@fontsource-variable/ubuntu-sans&apos;;

// Light mode colours
$LIGHT-SURFACE-1: hsl(220, 50%, 90%);
$LIGHT-SURFACE-2: hsl(220, 50%, 95%);
$LIGHT-SURFACE-3: hsl(220, 50%, 100%);
$LIGHT-SHADOW-1: hsla(220, 50%, 10%, 0.3);
$LIGHT-SHADOW-2: hsla(220, 50%, 10%, 0.15);
$SECONDARY-LIGHT: hsl(220, 50%, 10%);
$TERTIARY-LIGHT: hsl(280, 80%, 20%);
$ACCENT-LIGHT: hsl(160, 80%, 20%);
$DISABLED-LIGHT: hsl(220, 50%, 35%);

// Dark mode colours
$DARK-SURFACE-1: hsl(220, 50%, 10%);
$DARK-SURFACE-2: hsl(220, 50%, 15%);
$DARK-SURFACE-3: hsl(220, 50%, 20%);
$DARK-SHADOW-1: hsla(220, 50%, 90%, 0.3);
$DARK-SHADOW-2: hsla(220, 50%, 90%, 0.15);
$SECONDARY_DARK: hsl(220, 50%, 90%);
$TERTIARY-DARK: hsl(280, 80%, 80%);
$ACCENT-DARK: hsl(160, 80%, 80%);
$DISABLED-DARK: hsl(220, 50%, 75%);

:root {
  --surface-1: #{$LIGHT-SURFACE-1};
  --surface-2: #{$LIGHT-SURFACE-2};
  --surface-3: #{$LIGHT-SURFACE-3};
  --shadow-1: #{$LIGHT-SHADOW-1};
  --shadow-2: #{$LIGHT-SHADOW-2};
  --secondary: #{$SECONDARY-LIGHT};
  --tertiary: #{$TERTIARY-LIGHT};
  --accent: #{$ACCENT-LIGHT};
  --disabled: #{$DISABLED-LIGHT};	
}

@media (prefers-color-scheme: dark) {
  :root {
    --surface-1: #{$DARK-SURFACE-1};
    --surface-2: #{$DARK-SURFACE-2};
    --surface-3: #{$DARK-SURFACE-3};
    --shadow-1: #{$DARK-SHADOW-1};
    --shadow-2: #{$DARK-SHADOW-2};
    --secondary: #{$SECONDARY-DARK};
    --tertiary: #{$TERTIARY-DARK};
    --accent: #{$ACCENT-DARK};
    --disabled: #{$DISABLED-DARK};
  }
}

body {
  background-color: var(--surface-1);
  color: var(--secondary);
  font-family: &apos;Ubuntu Sans Variable&apos;, sans-serif;
}

html,
body {
  height: 100%;
  margin: 0;
}

ul,
ol {
  list-style: none;
  margin: 0;
  padding: 0;
}

a {
  color: var(--accent);
}
"><code><span class="hljs-comment">// src/lib/styles/main.scss</span>

@use <span class="hljs-string">'@fontsource-variable/ubuntu-sans'</span>;

<span class="hljs-comment">// Light mode colours</span>
$LIGHT<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-1</span>: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">90</span><span class="hljs-operator">%</span>);
$LIGHT<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-2</span>: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">95</span><span class="hljs-operator">%</span>);
$LIGHT<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-3</span>: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">100</span><span class="hljs-operator">%</span>);
$LIGHT<span class="hljs-operator">-</span>SHADOW<span class="hljs-number">-1</span>: hsla(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">10</span><span class="hljs-operator">%</span>, <span class="hljs-number">0</span><span class="hljs-number">.3</span>);
$LIGHT<span class="hljs-operator">-</span>SHADOW<span class="hljs-number">-2</span>: hsla(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">10</span><span class="hljs-operator">%</span>, <span class="hljs-number">0</span><span class="hljs-number">.15</span>);
$SECONDARY<span class="hljs-operator">-</span>LIGHT: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">10</span><span class="hljs-operator">%</span>);
$TERTIARY<span class="hljs-operator">-</span>LIGHT: hsl(<span class="hljs-number">280</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>, <span class="hljs-number">20</span><span class="hljs-operator">%</span>);
$ACCENT<span class="hljs-operator">-</span>LIGHT: hsl(<span class="hljs-number">160</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>, <span class="hljs-number">20</span><span class="hljs-operator">%</span>);
$DISABLED<span class="hljs-operator">-</span>LIGHT: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">35</span><span class="hljs-operator">%</span>);

<span class="hljs-comment">// Dark mode colours</span>
$DARK<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-1</span>: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">10</span><span class="hljs-operator">%</span>);
$DARK<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-2</span>: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">15</span><span class="hljs-operator">%</span>);
$DARK<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-3</span>: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">20</span><span class="hljs-operator">%</span>);
$DARK<span class="hljs-operator">-</span>SHADOW<span class="hljs-number">-1</span>: hsla(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">90</span><span class="hljs-operator">%</span>, <span class="hljs-number">0</span><span class="hljs-number">.3</span>);
$DARK<span class="hljs-operator">-</span>SHADOW<span class="hljs-number">-2</span>: hsla(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">90</span><span class="hljs-operator">%</span>, <span class="hljs-number">0</span><span class="hljs-number">.15</span>);
$SECONDARY_DARK: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">90</span><span class="hljs-operator">%</span>);
$TERTIARY<span class="hljs-operator">-</span>DARK: hsl(<span class="hljs-number">280</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>);
$ACCENT<span class="hljs-operator">-</span>DARK: hsl(<span class="hljs-number">160</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>, <span class="hljs-number">80</span><span class="hljs-operator">%</span>);
$DISABLED<span class="hljs-operator">-</span>DARK: hsl(<span class="hljs-number">220</span>, <span class="hljs-number">50</span><span class="hljs-operator">%</span>, <span class="hljs-number">75</span><span class="hljs-operator">%</span>);

:root {
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span>surface<span class="hljs-number">-1</span>: #{$LIGHT<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-1</span>};
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span>surface<span class="hljs-number">-2</span>: #{$LIGHT<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-2</span>};
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span>surface<span class="hljs-number">-3</span>: #{$LIGHT<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-3</span>};
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span>shadow<span class="hljs-number">-1</span>: #{$LIGHT<span class="hljs-operator">-</span>SHADOW<span class="hljs-number">-1</span>};
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span>shadow<span class="hljs-number">-2</span>: #{$LIGHT<span class="hljs-operator">-</span>SHADOW<span class="hljs-number">-2</span>};
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span>secondary: #{$SECONDARY<span class="hljs-operator">-</span>LIGHT};
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span>tertiary: #{$TERTIARY<span class="hljs-operator">-</span>LIGHT};
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span>accent: #{$ACCENT<span class="hljs-operator">-</span>LIGHT};
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span>disabled: #{$DISABLED<span class="hljs-operator">-</span>LIGHT};	
}

@media (prefers<span class="hljs-operator">-</span>color<span class="hljs-operator">-</span>scheme: dark) {
  :root {
    <span class="hljs-operator">-</span><span class="hljs-operator">-</span>surface<span class="hljs-number">-1</span>: #{$DARK<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-1</span>};
    <span class="hljs-operator">-</span><span class="hljs-operator">-</span>surface<span class="hljs-number">-2</span>: #{$DARK<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-2</span>};
    <span class="hljs-operator">-</span><span class="hljs-operator">-</span>surface<span class="hljs-number">-3</span>: #{$DARK<span class="hljs-operator">-</span>SURFACE<span class="hljs-number">-3</span>};
    <span class="hljs-operator">-</span><span class="hljs-operator">-</span>shadow<span class="hljs-number">-1</span>: #{$DARK<span class="hljs-operator">-</span>SHADOW<span class="hljs-number">-1</span>};
    <span class="hljs-operator">-</span><span class="hljs-operator">-</span>shadow<span class="hljs-number">-2</span>: #{$DARK<span class="hljs-operator">-</span>SHADOW<span class="hljs-number">-2</span>};
    <span class="hljs-operator">-</span><span class="hljs-operator">-</span>secondary: #{$SECONDARY<span class="hljs-operator">-</span>DARK};
    <span class="hljs-operator">-</span><span class="hljs-operator">-</span>tertiary: #{$TERTIARY<span class="hljs-operator">-</span>DARK};
    <span class="hljs-operator">-</span><span class="hljs-operator">-</span>accent: #{$ACCENT<span class="hljs-operator">-</span>DARK};
    <span class="hljs-operator">-</span><span class="hljs-operator">-</span>disabled: #{$DISABLED<span class="hljs-operator">-</span>DARK};
  }
}

body {
  background<span class="hljs-operator">-</span>color: <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>surface<span class="hljs-number">-1</span>);
  color: <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>secondary);
  font<span class="hljs-operator">-</span>family: <span class="hljs-string">'Ubuntu Sans Variable'</span>, sans<span class="hljs-operator">-</span>serif;
}

html,
body {
  height: <span class="hljs-number">100</span><span class="hljs-operator">%</span>;
  margin: <span class="hljs-number">0</span>;
}

ul,
ol {
  list<span class="hljs-operator">-</span>style: none;
  margin: <span class="hljs-number">0</span>;
  padding: <span class="hljs-number">0</span>;
}

a {
  color: <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>accent);
}
</code></pre><p>Firstly, the font installed using <code>fontsource</code> was imported with the <code>use</code> keyword. Next, all the colours were declared as SCSS variables and a new colour was included for disabled buttons and fields for both the light and dark themes. The next two sections provide the scaffolding for theming changes to be toggled in the future. Presently, the default colour palette is set to light mode, however, when the user has dark mode enabled on their browser the dark theme colour palette overrides these values. Therefore by default, the theme of the website will be the same as the theme used by the browser. The CSS variables declared in the <code>:root</code> will be available to all our components as they are made available globally through inheritance.</p><p>Then the background colour, font colour and font style are all set in the <code>body</code>. Note <code>fontsource</code> will provide instructions for the font you have installed, as my font was serif, <code>sans-serif</code> is required in its declaration.</p><p>Finally, the height of the <code>html</code> and <code>body</code> elements are set to <code>100%</code> to ensure the app takes the full height of the screen. Additionally, list styling and padding are removed by default for all <code>ol</code> and <code>ul</code> elements. This is for convenience, the nav bar and the footer components will use these lists and we would have to remove the default styling in each component, so it saves time removing globally. Also the the color of hyperlinks is changed to better fit the theme we have selected.</p><h2 id="h-sveltekit-route-files" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">SvelteKit Route Files</h2><p>It is time to create the landing page. This page will go in the <code>routes</code> directory, currently, there is one SvelteKit route file <code>+page.svelte</code> in this directory. Route files are marked with a <code>+</code> prefix in the file name. As SvleteKit uses filesystem-based routing, this <code>+page.svelte</code> file is the page that will load at the root URL <code>https:localhost:5173/</code>.</p><p>SvelteKit also offers a <code>+layout.svlete</code> file, which enables you to build a template to contain reoccurring elements that remain present between different pages such as a navbar or footer. Any good homepage will have a nav and a footer so let’s build out the <code>+layout.svelte</code> file.</p><pre data-type="codeBlock" text="// src/routes/+layout.svelte

&lt;script&gt;
  import &apos;$lib/styles/main.scss&apos;;
&lt;/script&gt;

&lt;main&gt;
  &lt;slot /&gt;
&lt;/main&gt;
"><code><span class="hljs-comment">// src/routes/+layout.svelte</span>

<span class="hljs-operator">&#x3C;</span>script<span class="hljs-operator">></span>
  <span class="hljs-keyword">import</span> <span class="hljs-string">'$lib/styles/main.scss'</span>;
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>main<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>slot <span class="hljs-operator">/</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>main<span class="hljs-operator">></span>
</code></pre><p>They <code>+layout</code> also acts as an entry point for the <code>main.scss</code> file. Upon saving this you should notice the styles we added come to life. Additionally, a <code>main</code> tag was added to wrap the <code>slot</code>. This is for <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developers.google.com/search/docs/fundamentals/seo-starter-guide">SEO</a> purposes. <code>slot</code> is the tag that tells the Svelte compiler where the contents of the <code>+page.svelte</code> should be inserted within the structure of the <code>+layout.svelte</code> file.</p><h2 id="h-building-the-landing-page" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Building the Landing Page</h2><h3 id="h-nav-bar" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Nav bar</h3><p>Now that the structure has been set it is time to start constructing the various pieces that the landing page is composed of, starting with the navbar. Navbars are on every page and look simple enough, but are always more time-consuming than you think in my experience. In any case, in the <code>routes</code> directory, a <code>Navbar.svelte</code> file can be added.</p><pre data-type="codeBlock" text="// src/routes/Navbar.svelte

&lt;nav&gt;
  &lt;h2&gt;&lt;a href=&quot;/&quot;&gt;Go Fund Yourself&lt;/a&gt;&lt;/h2&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;/fund-yourself&quot;&gt;Fund yourself&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;/fund-someone&quot;&gt;Fund someone&lt;/a&gt;&lt;/li&gt;
      &lt;button&gt;connect&lt;/button&gt;
    &lt;/ul&gt;
&lt;/nav&gt;
"><code><span class="hljs-comment">// src/routes/Navbar.svelte</span>

<span class="hljs-operator">&#x3C;</span>nav<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>h2<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span>a href<span class="hljs-operator">=</span><span class="hljs-string">"/"</span><span class="hljs-operator">></span>Go Fund Yourself<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h2<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>ul<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>li<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span>a href<span class="hljs-operator">=</span><span class="hljs-string">"/fund-yourself"</span><span class="hljs-operator">></span>Fund yourself<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>li<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>li<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span>a href<span class="hljs-operator">=</span><span class="hljs-string">"/fund-someone"</span><span class="hljs-operator">></span>Fund someone<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>li<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>button<span class="hljs-operator">></span>connect<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>ul<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>nav<span class="hljs-operator">></span>
</code></pre><p>Okay, it is important to note that SvelteKit has no specialised <code>link</code> component to change the route, it is all done via the <code>a</code> tag. Therefore, clicking the logo will take you to the home page or root URL <code>/</code>. Conversely, clicking the two list items will take you to different pages, where you can create and donate to different fund-raises respectively. Clicking on these two links will result in a 404 error, but these pages will be built later to fix this error. You can ignore it for now.</p><p>The navbar can now be added to the <code>+layout.svelte</code> file so that it is rendered to the main page.</p><pre data-type="codeBlock" text="// src/routes/+layout.svelte

&lt;script&gt;
  import &apos;$lib/styles/main.scss&apos;;
  import Navbar from &apos;./Navbar.svelte&apos;;
&lt;/script&gt;

&lt;Navbar /&gt;
&lt;main&gt;
  &lt;slot /&gt;
&lt;/main&gt;
"><code><span class="hljs-comment">// src/routes/+layout.svelte</span>

<span class="hljs-operator">&#x3C;</span>script<span class="hljs-operator">></span>
  <span class="hljs-keyword">import</span> <span class="hljs-string">'$lib/styles/main.scss'</span>;
  <span class="hljs-keyword">import</span> <span class="hljs-title">Navbar</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./Navbar.svelte'</span>;
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>Navbar <span class="hljs-operator">/</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>main<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>slot <span class="hljs-operator">/</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>main<span class="hljs-operator">></span>
</code></pre><p>I think you’ll agree that in its present state, the navbar looks somewhere between plain to hideous, so let’s add some CSS to change that.</p><pre data-type="codeBlock" text="// src/routes/Navbar.svelte

&lt;nav&gt;
  &lt;h2&gt;&lt;a id=&quot;nav-logo&quot; href=&quot;/&quot;&gt;Go Fund Yourself&lt;/a&gt;&lt;/h2&gt;
    &lt;ul id=&quot;links-container&quot;&gt;
      &lt;li&gt;&lt;a href=&quot;/fund-yourself&quot;&gt;Fund yourself&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;/fund-someone&quot;&gt;Fund someone&lt;/a&gt;&lt;/li&gt;
      &lt;button&gt;Connect&lt;/button&gt;
    &lt;/ul&gt;
&lt;/nav&gt;

&lt;style scoped lang=&quot;scss&quot;&gt;
  nav {
    align-items: center; 
    display: flex;
    flex-direction: row;
    gap: 2.5rem;
    margin-inline: auto;
    position: sticky;
    top: 0;
    width: 100%;
    z-index: 1;

    #nav-logo {
      display: block;
      position: relative;
      width: max-content;
    }

    ul {
      align-items: center;
      display: flex;
      gap: 2rem;
      position: relative;
      width: 100%;
    }
    
    button {
      background-color: var(--tertiary);
      border-radius: 6px;
      border: none;
      color: var(--surface-1);
      cursor: pointer;
      font-weight: 550;
      margin-left: auto;
      padding: 10px 12px;
      width: 100px;
    }
  }
&lt;/style&gt;
"><code><span class="hljs-comment">// src/routes/Navbar.svelte</span>

<span class="hljs-operator">&#x3C;</span>nav<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>h2<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span>a id<span class="hljs-operator">=</span><span class="hljs-string">"nav-logo"</span> href<span class="hljs-operator">=</span><span class="hljs-string">"/"</span><span class="hljs-operator">></span>Go Fund Yourself<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h2<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>ul id<span class="hljs-operator">=</span><span class="hljs-string">"links-container"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>li<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span>a href<span class="hljs-operator">=</span><span class="hljs-string">"/fund-yourself"</span><span class="hljs-operator">></span>Fund yourself<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>li<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>li<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span>a href<span class="hljs-operator">=</span><span class="hljs-string">"/fund-someone"</span><span class="hljs-operator">></span>Fund someone<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>li<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>button<span class="hljs-operator">></span>Connect<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>ul<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>nav<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>style scoped lang<span class="hljs-operator">=</span><span class="hljs-string">"scss"</span><span class="hljs-operator">></span>
  nav {
    align<span class="hljs-operator">-</span>items: center; 
    display: flex;
    flex<span class="hljs-operator">-</span>direction: row;
    gap: <span class="hljs-number">2</span>.5rem;
    margin<span class="hljs-operator">-</span>inline: auto;
    position: sticky;
    top: <span class="hljs-number">0</span>;
    width: <span class="hljs-number">100</span><span class="hljs-operator">%</span>;
    z<span class="hljs-operator">-</span>index: <span class="hljs-number">1</span>;

    #nav<span class="hljs-operator">-</span>logo {
      display: <span class="hljs-built_in">block</span>;
      position: relative;
      width: max<span class="hljs-operator">-</span>content;
    }

    ul {
      align<span class="hljs-operator">-</span>items: center;
      display: flex;
      gap: 2rem;
      position: relative;
      width: <span class="hljs-number">100</span><span class="hljs-operator">%</span>;
    }
    
    button {
      background<span class="hljs-operator">-</span>color: <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>tertiary);
      border<span class="hljs-operator">-</span>radius: 6px;
      border: none;
      color: <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>surface<span class="hljs-number">-1</span>);
      cursor: pointer;
      font<span class="hljs-operator">-</span>weight: <span class="hljs-number">550</span>;
      margin<span class="hljs-operator">-</span>left: auto;
      padding: 10px 12px;
      width: 100px;
    }
  }
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>style<span class="hljs-operator">></span>
</code></pre><p>Further changes will be added to the navbar, but this will do for now.</p><h3 id="h-footer" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Footer</h3><p>Footers are quite simple, as done with the navbar before it, a <code>Footer.svelte</code> file was created in the <code>routes</code> directory.</p><pre data-type="codeBlock" text="// src/routes/Footer.svelte

&lt;footer id=&quot;site-footer&quot;&gt;
  &lt;section id=&quot;top-row&quot;&gt;
    &lt;h2 class=&quot;h3&quot;&gt;Go Fund Yourself&lt;/h2&gt;
  &lt;/section&gt;
  &lt;section id=&quot;bottom-row&quot;&gt;
    &lt;div id=&quot;funding-option&quot;&gt;
      &lt;h3 class=&quot;h6&quot;&gt;Funding options&lt;/h3&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;a href=&quot;/fund-yourself&quot;&gt;Start you own fund raise&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/fund-someone&quot;&gt;Support another cause&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
    &lt;div id=&quot;about-creator&quot;&gt;
      &lt;ul&gt;
    &lt;li&gt;Go Fund Yourself &amp;copy {new Date().getFullYear()}&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;
&lt;/footer&gt;

&lt;style scoped lang=&quot;scss&quot;&gt;
  #site-footer {
    color: var(--surface-1);
    display: flex;
    flex-direction: column;
    padding-top: 2.5rem;
    padding-bottom: 2.5rem;
    position: relative;
    
    #bottom-row {
      display: flex;
      flex-direction: row;
      gap: 4rem;

      #about-creator {
    align-content: end;
        
        p {
      margin-bottom: 4px;
    }
      }
    }

    li {
      margin-bottom: 0.25rem;
    }
    
    a {
      color: var(--surface-1);
      text-decoration: none;
      
      &amp;:hover {
        color: var(--tertiary);
      }
    }

    &amp;::before {
      content: &apos;&apos;;
      background-color: var(--secondary);
      height: 100%;
      left: 50%;
      position: absolute;
      top: 0;  
      transform: translateX(-50%);
      width: 100vw;
      z-index: -1;
    }
  }
&lt;/style&gt;
"><code><span class="hljs-comment">// src/routes/Footer.svelte</span>

<span class="hljs-operator">&#x3C;</span>footer id<span class="hljs-operator">=</span><span class="hljs-string">"site-footer"</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>section id<span class="hljs-operator">=</span><span class="hljs-string">"top-row"</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>h2 class<span class="hljs-operator">=</span><span class="hljs-string">"h3"</span><span class="hljs-operator">></span>Go Fund Yourself<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h2<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>section<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>section id<span class="hljs-operator">=</span><span class="hljs-string">"bottom-row"</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"funding-option"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>h3 class<span class="hljs-operator">=</span><span class="hljs-string">"h6"</span><span class="hljs-operator">></span>Funding options<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h3<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>ul<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>li<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span>a href<span class="hljs-operator">=</span><span class="hljs-string">"/fund-yourself"</span><span class="hljs-operator">></span>Start you own fund raise<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>li<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>li<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span>a href<span class="hljs-operator">=</span><span class="hljs-string">"/fund-someone"</span><span class="hljs-operator">></span>Support another cause<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>li<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>ul<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"about-creator"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>ul<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>li<span class="hljs-operator">></span>Go Fund Yourself <span class="hljs-operator">&#x26;</span>copy {<span class="hljs-keyword">new</span> Date().getFullYear()}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>li<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>ul<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>section<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>footer<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>style scoped lang<span class="hljs-operator">=</span><span class="hljs-string">"scss"</span><span class="hljs-operator">></span>
  #site<span class="hljs-operator">-</span>footer {
    color: <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>surface<span class="hljs-number">-1</span>);
    display: flex;
    flex<span class="hljs-operator">-</span>direction: column;
    padding<span class="hljs-operator">-</span>top: <span class="hljs-number">2</span>.5rem;
    padding<span class="hljs-operator">-</span>bottom: <span class="hljs-number">2</span>.5rem;
    position: relative;
    
    #bottom<span class="hljs-operator">-</span>row {
      display: flex;
      flex<span class="hljs-operator">-</span>direction: row;
      gap: 4rem;

      #about<span class="hljs-operator">-</span>creator {
    align<span class="hljs-operator">-</span>content: end;
        
        p {
      margin<span class="hljs-operator">-</span>bottom: 4px;
    }
      }
    }

    li {
      margin<span class="hljs-operator">-</span>bottom: <span class="hljs-number">0</span>.25rem;
    }
    
    a {
      color: <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>surface<span class="hljs-number">-1</span>);
      text<span class="hljs-operator">-</span>decoration: none;
      
      <span class="hljs-operator">&#x26;</span>:hover {
        color: <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>tertiary);
      }
    }

    <span class="hljs-operator">&#x26;</span>::before {
      content: <span class="hljs-string">''</span>;
      background<span class="hljs-operator">-</span>color: <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>secondary);
      height: <span class="hljs-number">100</span><span class="hljs-operator">%</span>;
      left: <span class="hljs-number">50</span><span class="hljs-operator">%</span>;
      position: absolute;
      top: <span class="hljs-number">0</span>;  
      transform: translateX(<span class="hljs-number">-50</span><span class="hljs-operator">%</span>);
      width: 100vw;
      z<span class="hljs-operator">-</span>index: <span class="hljs-number">-1</span>;
    }
  }
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>style<span class="hljs-operator">></span>
</code></pre><p>Perfect, this can now be imported into the <code>+layout.svelte</code> file to complete the basic scaffolding of our landing page. However, there is a noticeable issue:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4283a94908d658c6d273bebbecbff6b585724025009d07b7a684d3d015b93352.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>The <code>+layout.svelte</code> file can be modified to ensure that both the navbar and the footer are placed in their correct position at the top and bottom of the page respectively.</p><pre data-type="codeBlock" text="// src/routes/+layout.svelte

&lt;script&gt;
  import &apos;$lib/styles/main.scss&apos;;
  import Navbar from &apos;./Navbar.svelte&apos;;
  import Footer from &apos;./Footer.svelte&apos;;
&lt;/script&gt;

&lt;div class=&quot;container&quot;&gt;
  &lt;Navbar /&gt;
  &lt;main&gt;
    &lt;slot /&gt;
  &lt;/main&gt;
  &lt;Footer /&gt;
&lt;/div&gt;

&lt;style scoped lang=&quot;scss&quot;&gt;
  .container {
    display: grid;
    grid-template-rows: auto 1fr auto;
    height: 100%;
    margin-inline: auto;
    max-inline-size: 1080px;
    padding-inline: 2rem;
  }
&lt;/style&gt;
"><code><span class="hljs-comment">// src/routes/+layout.svelte</span>

<span class="hljs-operator">&#x3C;</span>script<span class="hljs-operator">></span>
  <span class="hljs-keyword">import</span> <span class="hljs-string">'$lib/styles/main.scss'</span>;
  <span class="hljs-keyword">import</span> <span class="hljs-title">Navbar</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./Navbar.svelte'</span>;
  <span class="hljs-keyword">import</span> <span class="hljs-title">Footer</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./Footer.svelte'</span>;
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"container"</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>Navbar <span class="hljs-operator">/</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>main<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>slot <span class="hljs-operator">/</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>main<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>Footer <span class="hljs-operator">/</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>style scoped lang<span class="hljs-operator">=</span><span class="hljs-string">"scss"</span><span class="hljs-operator">></span>
  .container {
    display: grid;
    grid<span class="hljs-operator">-</span>template<span class="hljs-operator">-</span>rows: auto 1fr auto;
    height: <span class="hljs-number">100</span><span class="hljs-operator">%</span>;
    margin<span class="hljs-operator">-</span>inline: auto;
    max<span class="hljs-operator">-</span>inline<span class="hljs-operator">-</span>size: 1080px;
    padding<span class="hljs-operator">-</span>inline: 2rem;
  }
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>style<span class="hljs-operator">></span>
</code></pre><p>Much better:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/365c5a8565a4e09767f31dfd03ca2e4d39345d5279a74215887a751c14a59f14.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-landing-page-content" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Landing Page Content</h3><p>When I was creating this project I was learning vim motions on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.vimified.com/">vimified</a>. Not only did it help me learn the shortcuts for vim, but it was also the inspiration for a simple yet effective landing page design.</p><pre data-type="codeBlock" text="// src/routes/+page.svelte

&lt;div id=&quot;landing-container&quot;&gt;
  &lt;section id=&quot;landing-section&quot;&gt;
    &lt;h1&gt;Go&lt;/h1&gt;
    &lt;h1&gt;Fund&lt;/h1&gt;
    &lt;h1&gt;Yourself.&lt;/h1&gt;

    &lt;div id=&quot;call-to-action&quot;&gt;
      &lt;button class=&quot;shadow-btn&quot;&gt;Raise funds&lt;/button&gt;
      &lt;p&gt;Harness the power of crowd-sourced fundraising today&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;
&lt;/div&gt;

&lt;style scoped lang=&quot;scss&quot;&gt;
  #landing-section {
    align-items: center;
    display: flex;
    flex-direction: column;
    justify-content: center;
    margin-top: 60px;
    padding: 2.5rem;
    position: relative;

    h1 {
      font-size: 108px;
      margin: 0;
    }

    #call-to-action {
      align-items: center;
      display: flex;
      flex-direction: column;
      gap: 1rem;
      justify-content: center;
      margin-top: 2.5rem;
      padding-top: 2.5rem;	
    }
  }
&lt;/style&gt;
"><code><span class="hljs-comment">// src/routes/+page.svelte</span>

<span class="hljs-operator">&#x3C;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"landing-container"</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>section id<span class="hljs-operator">=</span><span class="hljs-string">"landing-section"</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>h1<span class="hljs-operator">></span>Go<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>h1<span class="hljs-operator">></span>Fund<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>h1<span class="hljs-operator">></span>Yourself.&#x3C;<span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>

    <span class="hljs-operator">&#x3C;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"call-to-action"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>button class<span class="hljs-operator">=</span><span class="hljs-string">"shadow-btn"</span><span class="hljs-operator">></span>Raise funds<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>Harness the power of crowd<span class="hljs-operator">-</span>sourced fundraising today<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>section<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>style scoped lang<span class="hljs-operator">=</span><span class="hljs-string">"scss"</span><span class="hljs-operator">></span>
  #landing<span class="hljs-operator">-</span>section {
    align<span class="hljs-operator">-</span>items: center;
    display: flex;
    flex<span class="hljs-operator">-</span>direction: column;
    justify<span class="hljs-operator">-</span>content: center;
    margin<span class="hljs-operator">-</span>top: 60px;
    padding: <span class="hljs-number">2</span>.5rem;
    position: relative;

    h1 {
      font<span class="hljs-operator">-</span>size: 108px;
      margin: <span class="hljs-number">0</span>;
    }

    #call<span class="hljs-operator">-</span>to<span class="hljs-operator">-</span>action {
      align<span class="hljs-operator">-</span>items: center;
      display: flex;
      flex<span class="hljs-operator">-</span>direction: column;
      gap: 1rem;
      justify<span class="hljs-operator">-</span>content: center;
      margin<span class="hljs-operator">-</span>top: <span class="hljs-number">2</span>.5rem;
      padding<span class="hljs-operator">-</span>top: <span class="hljs-number">2</span>.5rem;	
    }
  }
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>style<span class="hljs-operator">></span>
</code></pre><p>You may have noticed that the class applied to the button is not present in the style of the component. Buttons are a key part of your application, they are a dynamic element and the element users interact with that action their requests. As such I have numerous different styles of buttons to convey the importance of the button and give it life.</p><pre data-type="codeBlock" text="// src/lib/styles/_buttons.scss

.connect-btn {
  background-color: var(--tertiary);
  border-radius: 6px;
  border: none;
  color: var(--surface-1);
  cursor: pointer;
  font-weight: 550;
  padding: 10px 12px;
  width: 100px;

  &amp;:hover {
    background-color: var(--tertiary);
    transition: background-color 0.2s;
  }
}

.connected-btn {
  align-items: center;
  background-color: transparent;
  border-radius: 6px;
  border: none;
  color: var(--secondary);
  cursor: pointer;
  display: flex;
  font-weight: 550;
  gap: 1rem;
  justify-content: center;
  padding: 0.75rem;
  width: min-content;

  img,
  svg {
    max-height: 20px;
    max-width: 20px;
  }

  @media (max-width: 680px) {
    padding: 0.25rem;
  }
}

.shadow-btn {
  background-color: var(--tertiary);
  border-radius: 8px;
  border: none;
  color: var(--surface-1);
  cursor: pointer;
  font-weight: 550;
  min-width: 100px;
  padding: 12px;

  &amp;:hover {
    box-shadow: 0px 0px 16px var(--tertiary);
  }
}

.power-btn {
  align-items: center;
  background-color: var(--surface-2);
  border: 1px solid var(--accent);
  border-radius: 12px;
  box-shadow: 0 2px var(--accent);
  color: var(--secondary);
  cursor: pointer;
  display: flex;
  gap: 0.5rem;
  padding: 12px 20px;
  
  &amp;:hover {
    box-shadow: 0 4px var(--accent);
  }

  &amp;:disabled {
    border: 1px solid var(--tertiary);
    box-shadow: 0 2px var(--tertiary);
  }

  img,
  svg {
    max-height: 16px;
    max-width: 16px;
  }
}

.table-filter-btn {
  background-color: transparent;
  border: none;
  color: var(--secondary);
  cursor: pointer;
  font-size: 14px;

  &amp;:hover {
    border-bottom: 2px solid var(--tertiary);
    margin-bottom: -2px;
  }
}

.pagination-btn {
  align-items: center;
  background-color: transparent;
  border: 1px solid transparent;
  color: var(--secondary);
  display: flex;
  font-size: 14px;
  gap: 0.5rem;
  padding: 12px;

  &amp;:hover:not(:disabled) {
    border-radius: 8px;
    border: 1px solid var(--secondary);
    cursor: pointer;
  }

  &amp;:disabled {
    color: var(--disabled);
  }

  img,
  svg {
    max-height: 1rem;
    max-width: 1rem;
  }
}

.back-btn {
  align-items: center;
  background-color: transparent;
  color: var(--secondary);
  display: flex;
  font-size: 14px;
  gap: 0.25rem;
  padding: 12px;
  text-decoration: none;

  &amp;:hover:not(:disabled) {
    text-decoration: underline;
    border-radius: 8px;
    cursor: pointer;
  }

  img,
  svg {
    max-height: 0.65rem;
    max-width: 0.65rem;
  }
}

.cancel-btn {
  background-color: transparent;
  border: 1px solid var(--secondary);
  border-radius: 8px;
  color: var(--secondary);
  cursor: pointer;
  padding: 0.75rem;

  &amp;:hover {
    background-color: var(--surface-1);
  }
}

.max-btn {
  background-color: transparent;
  border: none;
  cursor: pointer;
  padding: 0.5rem;

  &amp;:hover {
    text-decoration: underline;
  }
}
"><code>// <span class="hljs-attribute">src</span>/lib/styles/_buttons<span class="hljs-selector-class">.scss</span>

<span class="hljs-selector-class">.connect-btn</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--tertiary);
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">6px</span>;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--surface-<span class="hljs-number">1</span>);
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">550</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">12px</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100px</span>;

  &#x26;<span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--tertiary);
    <span class="hljs-attribute">transition</span>: background-color <span class="hljs-number">0.2s</span>;
  }
}

<span class="hljs-selector-class">.connected-btn</span> {
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">background-color</span>: transparent;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">6px</span>;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--secondary);
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">550</span>;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.75rem</span>;
  <span class="hljs-attribute">width</span>: min-content;

  <span class="hljs-selector-tag">img</span>,
  svg {
    <span class="hljs-attribute">max-height</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">20px</span>;
  }

  <span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width</span>: <span class="hljs-number">680px</span>) {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.25rem</span>;
  }
}

<span class="hljs-selector-class">.shadow-btn</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--tertiary);
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--surface-<span class="hljs-number">1</span>);
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">550</span>;
  <span class="hljs-attribute">min-width</span>: <span class="hljs-number">100px</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">12px</span>;

  &#x26;<span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0px</span> <span class="hljs-number">0px</span> <span class="hljs-number">16px</span> <span class="hljs-built_in">var</span>(--tertiary);
  }
}

<span class="hljs-selector-class">.power-btn</span> {
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--surface-<span class="hljs-number">2</span>);
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-built_in">var</span>(--accent);
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">12px</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-built_in">var</span>(--accent);
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--secondary);
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">0.5rem</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">12px</span> <span class="hljs-number">20px</span>;
  
  &#x26;<span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">4px</span> <span class="hljs-built_in">var</span>(--accent);
  }

  &#x26;<span class="hljs-selector-pseudo">:disabled</span> {
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-built_in">var</span>(--tertiary);
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-built_in">var</span>(--tertiary);
  }

  <span class="hljs-selector-tag">img</span>,
  svg {
    <span class="hljs-attribute">max-height</span>: <span class="hljs-number">16px</span>;
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">16px</span>;
  }
}

<span class="hljs-selector-class">.table-filter-btn</span> {
  <span class="hljs-attribute">background-color</span>: transparent;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--secondary);
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">14px</span>;

  &#x26;<span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">border-bottom</span>: <span class="hljs-number">2px</span> solid <span class="hljs-built_in">var</span>(--tertiary);
    <span class="hljs-attribute">margin-bottom</span>: -<span class="hljs-number">2px</span>;
  }
}

<span class="hljs-selector-class">.pagination-btn</span> {
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">background-color</span>: transparent;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid transparent;
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--secondary);
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">14px</span>;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">0.5rem</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">12px</span>;

  &#x26;<span class="hljs-selector-pseudo">:hover</span><span class="hljs-selector-pseudo">:not</span>(<span class="hljs-selector-pseudo">:disabled</span>) {
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-built_in">var</span>(--secondary);
    <span class="hljs-attribute">cursor</span>: pointer;
  }

  &#x26;<span class="hljs-selector-pseudo">:disabled</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--disabled);
  }

  <span class="hljs-selector-tag">img</span>,
  svg {
    <span class="hljs-attribute">max-height</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1rem</span>;
  }
}

<span class="hljs-selector-class">.back-btn</span> {
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">background-color</span>: transparent;
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--secondary);
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">14px</span>;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">0.25rem</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">12px</span>;
  <span class="hljs-attribute">text-decoration</span>: none;

  &#x26;<span class="hljs-selector-pseudo">:hover</span><span class="hljs-selector-pseudo">:not</span>(<span class="hljs-selector-pseudo">:disabled</span>) {
    <span class="hljs-attribute">text-decoration</span>: underline;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
  }

  <span class="hljs-selector-tag">img</span>,
  svg {
    <span class="hljs-attribute">max-height</span>: <span class="hljs-number">0.65rem</span>;
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">0.65rem</span>;
  }
}

<span class="hljs-selector-class">.cancel-btn</span> {
  <span class="hljs-attribute">background-color</span>: transparent;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-built_in">var</span>(--secondary);
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--secondary);
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.75rem</span>;

  &#x26;<span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--surface-<span class="hljs-number">1</span>);
  }
}

<span class="hljs-selector-class">.max-btn</span> {
  <span class="hljs-attribute">background-color</span>: transparent;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;

  &#x26;<span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">text-decoration</span>: underline;
  }
}
</code></pre><p>Importing these buttons into the <code>main.scss</code> will enable these classes to be used throughout the app.</p><pre data-type="codeBlock" text="// src/lib/styles/main.scss

@use &apos;buttons&apos;;

...
"><code>// <span class="hljs-attribute">src</span>/lib/styles/<span class="hljs-selector-tag">main</span><span class="hljs-selector-class">.scss</span>

<span class="hljs-keyword">@use</span> <span class="hljs-string">'buttons'</span>;

...
</code></pre><h3 id="h-animations-erm-i-mean-transitions" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Animations… erm I mean transitions</h3><p>Svelte has a fantastic offering of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://svelte.dev/docs/svelte/transition">transitions</a> to add life to different elements as they are added and removed to the DOM. A fade transition to the newly added <code>+page.svelte</code> content gives it more life and make the user’s entrance to the site feel more grandiose.</p><pre data-type="codeBlock" text="// src/routes/+pages.svelte

&lt;script&gt;
  import { onMount } from &apos;svelte&apos;;
  import { fade } from &apos;svelte/transition&apos;;

  // STATE VARS
  let landingPageVisible = false;

  // LIFECYCLE LOGIC
  onMount(() =&gt; {
    landingPageVisible = true;
    window.scrollTo(0, 0);
  });
&lt;/script&gt;

&lt;div id=&quot;landing-container&quot;&gt;
  &lt;section id=&quot;landing-section&quot;&gt;
    {#if landingPageVisible}
      &lt;h1 in:fade={{ delay: 100, duration: 1000 }}&gt;Go&lt;/h1&gt;
      &lt;h1 in:fade={{ delay: 800, duration: 1000 }}&gt;Fund&lt;/h1&gt;
      &lt;h1 in:fade={{ delay: 1500, duration: 1000 }}&gt;Yourself.&lt;/h1&gt;

      &lt;button 
        in:fade={{ delay: 2200, duration: 1000 }} 
        class=&quot;shadow-btn&quot;
      &gt;Raise funds&lt;/button&gt;
      &lt;p&gt;Harness the power of crowd-sourced fundraising today&lt;/p&gt;
    {/if}
  &lt;/section&gt;
&lt;/div&gt;
"><code><span class="hljs-comment">// src/routes/+pages.svelte</span>

<span class="hljs-operator">&#x3C;</span>script<span class="hljs-operator">></span>
  <span class="hljs-keyword">import</span> { <span class="hljs-title">onMount</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'svelte'</span>;
  <span class="hljs-keyword">import</span> { <span class="hljs-title">fade</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'svelte/transition'</span>;

  <span class="hljs-comment">// STATE VARS</span>
  let landingPageVisible <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;

  <span class="hljs-comment">// LIFECYCLE LOGIC</span>
  onMount(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    landingPageVisible <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    window.scrollTo(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
  });
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"landing-container"</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>section id<span class="hljs-operator">=</span><span class="hljs-string">"landing-section"</span><span class="hljs-operator">></span>
    {#<span class="hljs-keyword">if</span> landingPageVisible}
      <span class="hljs-operator">&#x3C;</span>h1 in:fade<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">100</span>, duration: <span class="hljs-number">1000</span> }}<span class="hljs-operator">></span>Go<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>h1 in:fade<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">800</span>, duration: <span class="hljs-number">1000</span> }}<span class="hljs-operator">></span>Fund<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>h1 in:fade<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">1500</span>, duration: <span class="hljs-number">1000</span> }}<span class="hljs-operator">></span>Yourself.&#x3C;<span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>

      <span class="hljs-operator">&#x3C;</span>button 
        in:fade<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">2200</span>, duration: <span class="hljs-number">1000</span> }} 
        class<span class="hljs-operator">=</span><span class="hljs-string">"shadow-btn"</span>
      <span class="hljs-operator">></span>Raise funds<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>Harness the power of crowd<span class="hljs-operator">-</span>sourced fundraising today<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
    {<span class="hljs-operator">/</span><span class="hljs-keyword">if</span>}
  <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>section<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
</code></pre><p><code>fade</code> comes from the <code>svlete/transitions</code> module, but it is possible to build <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://svelte.dev/docs/svelte/transition#Custom-transition-functions">custom transitions</a>, that uses JavaScript or CSS to provide the animation behaviour. This article <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://css-tricks.com/making-your-first-custom-svelte-transition/">here</a> explains custom transitions in depth.</p><p>The API for a custom transition is detailed below:</p><pre data-type="codeBlock" text="transition = (node: HTMLElement, params: any, options: { direction: &apos;in&apos; | &apos;out&apos; | &apos;both&apos; }) =&gt; {
    delay?: number,
    duration?: number,
    easing?: (t: number) =&gt; number,
    css?: (t: number, u: number) =&gt; string,
    tick?: (t: number, u: number) =&gt; void
}
"><code>transition = <span class="hljs-function">(<span class="hljs-params">node: HTMLElement, params: <span class="hljs-built_in">any</span>, options: { direction: <span class="hljs-string">'in'</span> | <span class="hljs-string">'out'</span> | <span class="hljs-string">'both'</span> }</span>) =></span> {
    delay?: <span class="hljs-built_in">number</span>,
    duration?: <span class="hljs-built_in">number</span>,
    easing?: <span class="hljs-function">(<span class="hljs-params">t: <span class="hljs-built_in">number</span></span>) =></span> <span class="hljs-built_in">number</span>,
    css?: <span class="hljs-function">(<span class="hljs-params">t: <span class="hljs-built_in">number</span>, u: <span class="hljs-built_in">number</span></span>) =></span> <span class="hljs-built_in">string</span>,
    tick?: <span class="hljs-function">(<span class="hljs-params">t: <span class="hljs-built_in">number</span>, u: <span class="hljs-built_in">number</span></span>) =></span> <span class="hljs-built_in">void</span>
}
</code></pre><p>A transition is a function that has the following properties</p><ul><li><p>Takes a parameter <code>node</code>, which is a HTML element to which the transition will be applied.</p></li><li><p>Can optionally, provide options for each stage of the transition, <code>in</code> when the element is being rendered, <code>out</code> when the component is being removed or <code>both</code> which encapsulates the previous two stages.</p></li><li><p>Optionally returns a <code>delay</code>, time in ms before transition runs.</p></li><li><p>Optionally returns a <code>duration</code>, the time in ms the transition will take to complete.</p></li><li><p>Optionally returns <code>easing</code>, the rate of change of the transition within the duration it runs. E.g. does the animation take linear time to complete or speed up or slow down throughout the duration it runs.</p></li><li><p>Must return either a <code>css</code> or <code>tick</code> callback that is the transition that will be applied to the element.</p></li></ul><p>Here is an example of a custom transition used in Go Fund Yourself:</p><pre data-type="codeBlock" text="// src/lib/animationsAndTransitions/typewriter.js

export default function typewriter(node, { delay = 0, speed = 2 }) {
  const valid = node.childNodes.length === 1 &amp;&amp; node.childNodes[0].nodeType === Node.TEXT_NODE;

  if (!valid) {
    throw new Error(`This transition only works on elements with a single text node child`);
  }

  const text = node.textContent;
  const duration = text.length / (speed * 0.01);

  return {
    delay,
    duration,
    tick: (t) =&gt; {
      const i = Math.trunc(text.length * t);
      node.textContent = text.slice(0, i);
    }
  };
}
"><code><span class="hljs-comment">// src/lib/animationsAndTransitions/typewriter.js</span>

export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">typewriter</span>(<span class="hljs-params">node, { delay = <span class="hljs-number">0</span>, speed = <span class="hljs-number">2</span> }</span>) </span>{
  const valid <span class="hljs-operator">=</span> node.childNodes.<span class="hljs-built_in">length</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> node.childNodes[<span class="hljs-number">0</span>].nodeType <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> Node.TEXT_NODE;

  <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>valid) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(`This transition only works on elements with a single text node child`);
  }

  const text <span class="hljs-operator">=</span> node.textContent;
  const duration <span class="hljs-operator">=</span> text.<span class="hljs-built_in">length</span> <span class="hljs-operator">/</span> (speed <span class="hljs-operator">*</span> <span class="hljs-number">0</span><span class="hljs-number">.01</span>);

  <span class="hljs-keyword">return</span> {
    delay,
    duration,
    tick: (t) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
      const i <span class="hljs-operator">=</span> Math.trunc(text.<span class="hljs-built_in">length</span> <span class="hljs-operator">*</span> t);
      node.textContent <span class="hljs-operator">=</span> text.slice(<span class="hljs-number">0</span>, i);
    }
  };
}
</code></pre><p>The transition above does the following:</p><ul><li><p>It checks that the HTML element only contains text to which the typewriter effect will be applied and throws an error if this is not fulfilled.</p></li><li><p>Stores the pure text in the var <code>text</code></p></li><li><p>Calculates duration based on the text length, the <code>speed</code> parameter which is defaulted to 2.</p></li><li><p>Defines JavaScript function for the effect. The <code>t</code> value increases from 0 to 1 over the duration of the transition, as this value increases, more characters are added to the string slice, giving the effect that the characters are being typed out.</p></li></ul><p>This can now be added to the call to action text below the button on the landing page on <code>+page.svelte</code> in the <code>routes</code> directory:</p><pre data-type="codeBlock" text="// src/routes/+page.svelte

...

&lt;div id=&quot;landing-container&quot;&gt;
  &lt;section id=&quot;landing-section&quot;&gt;
    {#if landingPageVisible}
      &lt;h1 in:fade={{ delay: 100, duration: 1000 }}&gt;Go&lt;/h1&gt;
      &lt;h1 in:fade={{ delay: 800, duration: 1000 }}&gt;Fund&lt;/h1&gt;
      &lt;h1 in:fade={{ delay: 1500, duration: 1000 }}&gt;Yourself.&lt;/h1&gt;

      &lt;button in:fade={{ delay: 2200, duration: 1000 }} class=&quot;shadow-btn&quot;&gt;Raise funds&lt;/button&gt;
      &lt;p in:typewriter={{ speed: 2, delay: 2250 }}&gt;
    Harness the power of crowd-sourced fundraising today
      &lt;/p&gt;
    {/if}
  &lt;/section&gt;
&lt;/div&gt;

...
"><code><span class="hljs-comment">// src/routes/+page.svelte</span>

...

<span class="hljs-operator">&#x3C;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"landing-container"</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>section id<span class="hljs-operator">=</span><span class="hljs-string">"landing-section"</span><span class="hljs-operator">></span>
    {#<span class="hljs-keyword">if</span> landingPageVisible}
      <span class="hljs-operator">&#x3C;</span>h1 in:fade<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">100</span>, duration: <span class="hljs-number">1000</span> }}<span class="hljs-operator">></span>Go<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>h1 in:fade<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">800</span>, duration: <span class="hljs-number">1000</span> }}<span class="hljs-operator">></span>Fund<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>h1 in:fade<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">1500</span>, duration: <span class="hljs-number">1000</span> }}<span class="hljs-operator">></span>Yourself.&#x3C;<span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>

      <span class="hljs-operator">&#x3C;</span>button in:fade<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">2200</span>, duration: <span class="hljs-number">1000</span> }} class<span class="hljs-operator">=</span><span class="hljs-string">"shadow-btn"</span><span class="hljs-operator">></span>Raise funds<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>p in:typewriter<span class="hljs-operator">=</span>{{ speed: <span class="hljs-number">2</span>, delay: <span class="hljs-number">2250</span> }}<span class="hljs-operator">></span>
    Harness the power of crowd<span class="hljs-operator">-</span>sourced fundraising today
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
    {<span class="hljs-operator">/</span><span class="hljs-keyword">if</span>}
  <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>section<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

...
</code></pre><h2 id="h-finishing-the-homepage" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Finishing the Homepage</h2><h3 id="h-promotional-content" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Promotional content</h3><p>Now that there is a compelling and professional landing area for the user when they first visit the site, it is time to complete the rest of the landing page. To complete the this page, additional promotional content with call-to-action buttons was added. This will provide an explanation of the core offerings of the platform with immediate access points to these offerings. With the additional material added, the landing page code now looks as follows:</p><pre data-type="codeBlock" text="&lt;script&gt;
  import { onMount } from &apos;svelte&apos;;
  import { fade } from &apos;svelte/transition&apos;;
  import { typewriter } from &apos;$lib&apos;;
  import { AidSVG, AnonymousSVG, ChainsSVG, GlobeSVG } from &apos;$lib/assets&apos;;

  // STATE VARS
  let landingPageVisible = false;
  
  // LIFECYCLE LOGIC
  onMount(() =&gt; {
    landingPageVisible = true;
    window.scrollTo(0, 0);
  });
&lt;/script&gt;

&lt;div id=&quot;landing-container&quot;&gt;
  &lt;section id=&quot;landing-section&quot;&gt;
    {#if landingPageVisible}
      &lt;h1 in:fade={{ delay: 100, duration: 1000 }}&gt;Go&lt;/h1&gt;
      &lt;h1 in:fade={{ delay: 800, duration: 1000 }}&gt;Fund&lt;/h1&gt;
      &lt;h1 in:fade={{ delay: 1500, duration: 1000 }}&gt;Yourself.&lt;/h1&gt;

      &lt;button in:fade={{ delay: 2200, duration: 1000 }} class=&quot;shadow-btn&quot;&gt;Raise funds&lt;/button&gt;
      &lt;p in:typewriter={{ delay: 2200 }}&gt;Harness the power of crowd-sourced fundraising today&lt;/p&gt;
    {/if}
  &lt;/section&gt;
  &lt;section class=&quot;promo-section&quot;&gt;
    &lt;div class=&quot;promo-container&quot; transition:fade&gt;
      &lt;div class=&quot;promo-card card&quot;&gt;
        &lt;h2&gt;Raise funds for a special cause&lt;/h2&gt;
        &lt;p&gt;
          This platform allows you to leverage the power of smart contracts on the blockchain to raise funds for any type of cause near and dear to your heart in a decentralized and permissionless manner.
        &lt;/p&gt;
        &lt;p&gt;
          This means that Go Fund Yourself facilitates fund raising that is censorship resistant and global in its out-reach. No one can take down your fund-raising initiative and anyone with	an internet connection can contribute to your cause.
        &lt;/p&gt;
        &lt;p&gt;Get started raising today.&lt;/p&gt;
        &lt;button 
          class=&quot;shadow-btn&quot; 
          on:click={() =&gt; goto(&apos;/fund-yourself&apos;)}
        &gt;Fund yourself&lt;/button&gt;
      &lt;/div&gt;
      &lt;div class=&quot;promo-icon-container&quot;&gt;
        &lt;div class=&quot;promo-icon-card card&quot;&gt;
      &lt;GlobeSVG /&gt;
      &lt;p&gt;Raise globally&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class=&quot;promo-icon-card card&quot;&gt;
      &lt;ChainsSVG /&gt;
      &lt;p&gt;Raise without censorship&lt;/p&gt;
    &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;
  &lt;section class=&quot;promo-section&quot;&gt;
    &lt;div class=&quot;promo-container&quot;&gt;
      &lt;div class=&quot;promo-card card&quot;&gt;
        &lt;h2&gt;Donate to initiatives around the world&lt;/h2&gt;
    &lt;p&gt;
      Donate to unique initiatives around the globe and aid different communities and people to help them achieve their goals.
        &lt;/p&gt;
    &lt;p&gt;
      Being built on top of blockchain technology donations are pseudo-anonymous by default. So you can donate to different causes with piece of mind that your identity is not broadcasted every time you make a donation.
    &lt;/p&gt;
    &lt;p&gt;Get started donating today.&lt;/p&gt;
    &lt;button 
          class=&quot;shadow-btn&quot; 
          on:click={() =&gt; goto(&apos;/fund-someone&apos;)}
        &gt;Fund someone&lt;/button&gt;
      &lt;/div&gt;
      &lt;div class=&quot;promo-icon-container&quot;&gt;
        &lt;div class=&quot;promo-icon-card card&quot;&gt;
      &lt;AidSVG /&gt;
      &lt;p&gt;Aid communities across the world&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class=&quot;promo-icon-card card&quot;&gt;
      &lt;AnonymousSVG /&gt;
      &lt;p&gt;Pseudo-anonymous donations by default&lt;/p&gt;
    &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;
&lt;/div&gt;

&lt;style scoped lang=&quot;scss&quot;&gt;
  #landing-section {
    align-items: center;
    display: flex;
    flex-direction: column;
    height: 80vh;
    justify-content: center;
    margin-top: 60px;
    padding: 2.5rem;
    position: relative;

    h1 {
      font-size: 108px;
      margin: 0;
    }

    button {
      margin-top: 5rem;
    }

    p {
      margin-bottom: 0;
      margin-top: 1rem;
    }
  }

  .promo-section {
    min-height: max-content;
    padding-block: 5rem;
    position: relative;
  }

  .card {
    background-color: var(--surface-2);
    border-radius: 20px;
    border-top: 4px solid var(--accent);
    box-shadow:
      0 2px 2px var(--shadow-1),
      0 4px 4px var(--shadow-2);
    padding: 2rem;
  }

  .promo-container {
    display: flex;
    gap: 2.5rem;

    .promo-card {
      flex: 1;

      button {
    margin: 1rem 0;
      }
    }

    .promo-icon-container {
      display: flex;
      flex-direction: column;
      gap: 1rem;

      .promo-icon-card {
        align-items: center;
    display: flex;
    flex-direction: column;
    flex: 1;
    height: auto;
    justify-content: center;
    padding: 1rem;

    p {
      font-weight: 500;
      margin-bottom: 0;
      max-width: 155px;
      text-align: center;
    }

    img {
      border-radius: 50%;
      max-height: 100px;
      max-width: 100px;
    }
      }
    }
  }
&lt;/style&gt;
"><code><span class="hljs-operator">&#x3C;</span>script<span class="hljs-operator">></span>
  <span class="hljs-keyword">import</span> { <span class="hljs-title">onMount</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'svelte'</span>;
  <span class="hljs-keyword">import</span> { <span class="hljs-title">fade</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'svelte/transition'</span>;
  <span class="hljs-keyword">import</span> { <span class="hljs-title">typewriter</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'$lib'</span>;
  <span class="hljs-keyword">import</span> { <span class="hljs-title">AidSVG</span>, <span class="hljs-title">AnonymousSVG</span>, <span class="hljs-title">ChainsSVG</span>, <span class="hljs-title">GlobeSVG</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'$lib/assets'</span>;

  <span class="hljs-comment">// STATE VARS</span>
  let landingPageVisible <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
  
  <span class="hljs-comment">// LIFECYCLE LOGIC</span>
  onMount(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    landingPageVisible <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    window.scrollTo(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
  });
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"landing-container"</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>section id<span class="hljs-operator">=</span><span class="hljs-string">"landing-section"</span><span class="hljs-operator">></span>
    {#<span class="hljs-keyword">if</span> landingPageVisible}
      <span class="hljs-operator">&#x3C;</span>h1 in:fade<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">100</span>, duration: <span class="hljs-number">1000</span> }}<span class="hljs-operator">></span>Go<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>h1 in:fade<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">800</span>, duration: <span class="hljs-number">1000</span> }}<span class="hljs-operator">></span>Fund<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>h1 in:fade<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">1500</span>, duration: <span class="hljs-number">1000</span> }}<span class="hljs-operator">></span>Yourself.&#x3C;<span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>

      <span class="hljs-operator">&#x3C;</span>button in:fade<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">2200</span>, duration: <span class="hljs-number">1000</span> }} class<span class="hljs-operator">=</span><span class="hljs-string">"shadow-btn"</span><span class="hljs-operator">></span>Raise funds<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>p in:typewriter<span class="hljs-operator">=</span>{{ delay: <span class="hljs-number">2200</span> }}<span class="hljs-operator">></span>Harness the power of crowd<span class="hljs-operator">-</span>sourced fundraising today<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
    {<span class="hljs-operator">/</span><span class="hljs-keyword">if</span>}
  <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>section<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>section class<span class="hljs-operator">=</span><span class="hljs-string">"promo-section"</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"promo-container"</span> transition:fade<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"promo-card card"</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>h2<span class="hljs-operator">></span>Raise funds <span class="hljs-keyword">for</span> a special cause<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h2<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>
          This platform allows you to leverage the power of smart contracts on the blockchain to raise funds <span class="hljs-keyword">for</span> any <span class="hljs-keyword">type</span> of cause near and dear to your heart in a decentralized and permissionless manner.
        &#x3C;<span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>
          This means that Go Fund Yourself facilitates fund raising that <span class="hljs-keyword">is</span> censorship resistant and <span class="hljs-keyword">global</span> in its out<span class="hljs-operator">-</span>reach. No one can take down your fund<span class="hljs-operator">-</span>raising initiative and anyone with	an internet connection can contribute to your cause.
        &#x3C;<span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>Get started raising today.&#x3C;<span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>button 
          class<span class="hljs-operator">=</span><span class="hljs-string">"shadow-btn"</span> 
          on:click<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">></span> goto(<span class="hljs-string">'/fund-yourself'</span>)}
        <span class="hljs-operator">></span>Fund yourself<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"promo-icon-container"</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"promo-icon-card card"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>GlobeSVG <span class="hljs-operator">/</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>Raise globally<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"promo-icon-card card"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>ChainsSVG <span class="hljs-operator">/</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>Raise without censorship<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>section<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>section class<span class="hljs-operator">=</span><span class="hljs-string">"promo-section"</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"promo-container"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"promo-card card"</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>h2<span class="hljs-operator">></span>Donate to initiatives around the world<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h2<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>
      Donate to unique initiatives around the globe and aid different communities and people to help them achieve their goals.
        &#x3C;<span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>
      Being built on top of blockchain technology donations are pseudo<span class="hljs-operator">-</span><span class="hljs-keyword">anonymous</span> by default. So you can donate to different causes with piece of mind that your identity <span class="hljs-keyword">is</span> not broadcasted every time you make a donation.
    &#x3C;<span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>Get started donating today.&#x3C;<span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>button 
          class<span class="hljs-operator">=</span><span class="hljs-string">"shadow-btn"</span> 
          on:click<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">></span> goto(<span class="hljs-string">'/fund-someone'</span>)}
        <span class="hljs-operator">></span>Fund someone<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"promo-icon-container"</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"promo-icon-card card"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>AidSVG <span class="hljs-operator">/</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>Aid communities across the world<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"promo-icon-card card"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>AnonymousSVG <span class="hljs-operator">/</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>Pseudo<span class="hljs-operator">-</span><span class="hljs-keyword">anonymous</span> donations by default<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>section<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>style scoped lang<span class="hljs-operator">=</span><span class="hljs-string">"scss"</span><span class="hljs-operator">></span>
  #landing<span class="hljs-operator">-</span>section {
    align<span class="hljs-operator">-</span>items: center;
    display: flex;
    flex<span class="hljs-operator">-</span>direction: column;
    height: 80vh;
    justify<span class="hljs-operator">-</span>content: center;
    margin<span class="hljs-operator">-</span>top: 60px;
    padding: <span class="hljs-number">2</span>.5rem;
    position: relative;

    h1 {
      font<span class="hljs-operator">-</span>size: 108px;
      margin: <span class="hljs-number">0</span>;
    }

    button {
      margin<span class="hljs-operator">-</span>top: 5rem;
    }

    p {
      margin<span class="hljs-operator">-</span>bottom: <span class="hljs-number">0</span>;
      margin<span class="hljs-operator">-</span>top: 1rem;
    }
  }

  .promo-section {
    min<span class="hljs-operator">-</span>height: max<span class="hljs-operator">-</span>content;
    padding<span class="hljs-operator">-</span><span class="hljs-built_in">block</span>: 5rem;
    position: relative;
  }

  .card {
    background<span class="hljs-operator">-</span>color: <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>surface<span class="hljs-number">-2</span>);
    border<span class="hljs-operator">-</span>radius: 20px;
    border<span class="hljs-operator">-</span>top: 4px solid <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>accent);
    box<span class="hljs-operator">-</span>shadow:
      <span class="hljs-number">0</span> 2px 2px <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>shadow<span class="hljs-number">-1</span>),
      <span class="hljs-number">0</span> 4px 4px <span class="hljs-keyword">var</span>(<span class="hljs-operator">-</span><span class="hljs-operator">-</span>shadow<span class="hljs-number">-2</span>);
    padding: 2rem;
  }

  .promo-container {
    display: flex;
    gap: <span class="hljs-number">2</span>.5rem;

    .promo-card {
      flex: <span class="hljs-number">1</span>;

      button {
    margin: 1rem <span class="hljs-number">0</span>;
      }
    }

    .promo-icon<span class="hljs-operator">-</span>container {
      display: flex;
      flex<span class="hljs-operator">-</span>direction: column;
      gap: 1rem;

      .promo-icon<span class="hljs-operator">-</span>card {
        align<span class="hljs-operator">-</span>items: center;
    display: flex;
    flex<span class="hljs-operator">-</span>direction: column;
    flex: <span class="hljs-number">1</span>;
    height: auto;
    justify<span class="hljs-operator">-</span>content: center;
    padding: 1rem;

    p {
      font<span class="hljs-operator">-</span>weight: <span class="hljs-number">500</span>;
      margin<span class="hljs-operator">-</span>bottom: <span class="hljs-number">0</span>;
      max<span class="hljs-operator">-</span>width: 155px;
      text<span class="hljs-operator">-</span>align: center;
    }

    img {
      border<span class="hljs-operator">-</span>radius: <span class="hljs-number">50</span><span class="hljs-operator">%</span>;
      max<span class="hljs-operator">-</span>height: 100px;
      max<span class="hljs-operator">-</span>width: 100px;
    }
      }
    }
  }
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>style<span class="hljs-operator">></span>
</code></pre><p><em>Please refer to this </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/MGM103/svelte5-onchain-app/tree/main/frontend-part-1/src/lib/assets"><em>link</em></a><em> for the code for SVG components that were added.</em></p><p>Okay, the content for the homepage is finished. However, just as was done for the landing section, animations can be added to the new content. To keep the UI decluttered and draw the user’s attention to the latest information, fade animations on scroll can be applied to the new content cards to fade in when visible and fade out when hidden.</p><h3 id="h-intersection-observer" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Intersection Observer</h3><p>How does one know when an element has scrolled into/out of view of the user? Fortunately, there is an established API supported by the major browsers that fulfils this purpose, the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#a_simple_example">intersection observer API</a>.</p><p>It is quite easy to work with this API, simply put by the MDN docs:</p><blockquote><p>The Intersection Observer API allows you to configure a callback that is called when either of these circumstances occur:</p><ul><li><p>A <strong>target</strong> element intersects either the device&apos;s viewport or a specified element.</p></li><li><p>The first time the observer is initially asked to watch a target element.</p></li></ul></blockquote><p>With this knowledge, intersection observers can be set up for both the fundraising and donation promotional sections. A state variable for each of these sections will be added, <code>fundRaisingPromoVisibile</code> and <code>donationPromoVisible</code>. These will initially be set to <code>false</code> and the class <code>promo-section</code> shared by the promo content will set the <code>opacity</code> to 0, ensuring the content is hidden initially.</p><pre data-type="codeBlock" text="// src/routes/+page.svelte

...

// STATE VARS
let landingPageVisible = false;
let fundRaisingPromoVisible = false;
let donationPromoVisible = false;

...

&lt;style scoped lang=&quot;scss&quot;&gt;
  ...

  .promo-section {
    height: max-content;
    opacity: 0;
    padding-block: 5rem;
    position: relative;
  }

  ...
&lt;/style&gt;
"><code><span class="hljs-comment">// src/routes/+page.svelte</span>

...

<span class="hljs-comment">// STATE VARS</span>
let landingPageVisible <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
let fundRaisingPromoVisible <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
let donationPromoVisible <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;

...

<span class="hljs-operator">&#x3C;</span>style scoped lang<span class="hljs-operator">=</span><span class="hljs-string">"scss"</span><span class="hljs-operator">></span>
  ...

  .promo-section {
    height: max<span class="hljs-operator">-</span>content;
    opacity: <span class="hljs-number">0</span>;
    padding<span class="hljs-operator">-</span><span class="hljs-built_in">block</span>: 5rem;
    position: relative;
  }

  ...
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>style<span class="hljs-operator">></span>
</code></pre><p>These observers will fire a callback function when the element is 40% viewable and will set the viewable state variable to true, triggering the fade-in animation on scroll. Conversely, when the element is not 40% viewable the callback should fire again setting the visible state variable to <code>false</code>.</p><p>To create an intersection observer requires calling the observer’s constructor passing it the aforementioned callback and some initialisation options that dictate the type of intersection the observer is monitoring.</p><pre data-type="codeBlock" text="const observerOptions = {
  root: null,
  rootMargin: &apos;0px&apos;,
  threshold: [0, 0.2, 0.4, 0.6, 0.8, 1]
};
"><code>const <span class="hljs-attr">observerOptions</span> = {
  root: null,
  rootMargin: '0px',
  threshold: <span class="hljs-section">[0, 0.2, 0.4, 0.6, 0.8, 1]</span>
}<span class="hljs-comment">;</span>
</code></pre><p>These are the options passed to the observer constructor on the <code>+page.svelte</code>. The <code>root</code> refers to the element that is being intersected with or that is used to measure the visibility of the target element. If left <code>null</code> the root becomes the viewport itself or the content viewable on the screen by the user. <code>rootMargin</code> allow a margin around the root element to be added to expand or contract the viewable area of the target element. Finally, <code>threshold</code> is the ratio of the visibility of the target element again the root element. If an element is half viewable its threshold value is <code>0.5</code> if fully viewable <code>1</code> and if hidden <code>0</code>. By providing an array of values for the <code>threshold</code>, the observer is able to update the visibility of the target element between the ticks of the provided threshold. In this case increments of 20%. For a practical demonstration of thresholds please refer to the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#intersection_observer_concepts_and_usage">MDN playground example</a> in this article.</p><p>To instantiate an intersection observer is quite simple:</p><pre data-type="codeBlock" text="const observer = new IntersectionObserver(callback, options);
"><code>const <span class="hljs-attr">observer</span> = new IntersectionObserver(callback, options)<span class="hljs-comment">;</span>
</code></pre><p>The options are ready, time to create the callback functions. Now as aforementioned, the goal of these callback functions is to set the visibility state variables of the content to <code>true</code> when 40% visible and false when less than 40% visible. This looks as follows:</p><pre data-type="codeBlock" text="// src/routes/+page.svelte

...

const showFundRaisingPromo = (entries) =&gt; {
  const [entry] = entries;

  if(entry.intersectionRatio &gt;= 0.4) {
    fundRaisingPromoVisible = true;
  } else {
    fundRaisingPromoVisible = false;
  }
}

const renderDonationPromo = (entries) =&gt; {
  const [entry] = entries;

  if (entry.intersectionRatio &gt;= 0.4) {
    donationPromoVisible = true;
  } else {
    donationPromoVisible = false;
  }
};
"><code><span class="hljs-comment">// src/routes/+page.svelte</span>

...

const showFundRaisingPromo <span class="hljs-operator">=</span> (entries) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
  const [entry] <span class="hljs-operator">=</span> entries;

  <span class="hljs-keyword">if</span>(entry.intersectionRatio <span class="hljs-operator">></span><span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.4</span>) {
    fundRaisingPromoVisible <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
  } <span class="hljs-keyword">else</span> {
    fundRaisingPromoVisible <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
  }
}

const renderDonationPromo <span class="hljs-operator">=</span> (entries) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
  const [entry] <span class="hljs-operator">=</span> entries;

  <span class="hljs-keyword">if</span> (entry.intersectionRatio <span class="hljs-operator">></span><span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.4</span>) {
    donationPromoVisible <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
  } <span class="hljs-keyword">else</span> {
    donationPromoVisible <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
  }
};
</code></pre><p>Intersection observer callbacks have an argument <code>entries</code> that contains a multitude of data about the intersection details of the target element. You can read more about this <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry">here</a>.</p><p><code>intersectionRatio</code> is the ratio of the target element&apos;s visibility against the root element. For example, if the root element was the viewport and half the target element was visible, the intersection ratio would be 0.5.</p><p>Now that the callbacks are prepared it is time to create the intersection observers to monitor the two content sections. These observers will be created in the <code>onMount</code> portion of the <code>+page.svelte</code>.</p><pre data-type="codeBlock" text="// src/routes/+page.svelte

...

// STATE VARS
let landingPageVisible = false;
let fundRaisingPromoVisible = false;
let donationPromoVisible = false;
let fundRaisingContainer;
let donationContainer;

// LIFECYCLE LOGIC
onMount(() =&gt; {
  landingPageVisible = true;
  window.scrollTo(0, 0);

  const observerOptions = {
    root: null,
    rootMargin: &apos;0px&apos;,
    threshold: [0, 0.2, 0.4, 0.6, 0.8, 1]
  };

  const fundRaisingPromoObserver = new IntersectionObserver(
    renderFundRaisingPromo,
    observerOptions
  );

  const donationPromoObserver = new IntersectionObserver(
    renderDonationPromo, 
    observerOptions
  );

  fundRaisingPromoObserver.observe(fundRaisingContainer);
  donationPromoObserver.observe(donationContainer);

  return () =&gt; {
    fundRaisingPromoObserver.unobserve(fundRaisingContainer);
    donationPromoObserver.unobserve(donationContainer);

    fundRaisingPromoObserver.disconnect();
    donationPromoObserver.disconnect();
  };
});

// UTILITY FUNCTIONS
const renderFundRaisingPromo = (entries) =&gt; {
  const [entry] = entries;

  if (entry.intersectionRatio &gt;= 0.4) {
    fundRaisingPromoVisible = true;
    console.log(&apos;Fund-raising promo is visible&apos;);
  } else {
    fundRaisingPromoVisible = false;
    console.log(&apos;Fund-raising promo is not visible&apos;);
  }
};

const renderDonationPromo = (entries) =&gt; {
  const [entry] = entries;

  if (entry.intersectionRatio &gt;= 0.4) {
    donationPromoVisible = true;
    console.log(&apos;Donation promo is visible&apos;);
  } else {
    donationPromoVisible = false;
    console.log(&apos;Donation promo is not visible&apos;);
  }
};
"><code><span class="hljs-comment">// src/routes/+page.svelte</span>

...

<span class="hljs-comment">// STATE VARS</span>
let landingPageVisible <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
let fundRaisingPromoVisible <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
let donationPromoVisible <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
let fundRaisingContainer;
let donationContainer;

<span class="hljs-comment">// LIFECYCLE LOGIC</span>
onMount(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
  landingPageVisible <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
  window.scrollTo(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

  const observerOptions <span class="hljs-operator">=</span> {
    root: null,
    rootMargin: <span class="hljs-string">'0px'</span>,
    threshold: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span><span class="hljs-number">.2</span>, <span class="hljs-number">0</span><span class="hljs-number">.4</span>, <span class="hljs-number">0</span><span class="hljs-number">.6</span>, <span class="hljs-number">0</span><span class="hljs-number">.8</span>, <span class="hljs-number">1</span>]
  };

  const fundRaisingPromoObserver <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IntersectionObserver(
    renderFundRaisingPromo,
    observerOptions
  );

  const donationPromoObserver <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IntersectionObserver(
    renderDonationPromo, 
    observerOptions
  );

  fundRaisingPromoObserver.observe(fundRaisingContainer);
  donationPromoObserver.observe(donationContainer);

  <span class="hljs-keyword">return</span> () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    fundRaisingPromoObserver.unobserve(fundRaisingContainer);
    donationPromoObserver.unobserve(donationContainer);

    fundRaisingPromoObserver.disconnect();
    donationPromoObserver.disconnect();
  };
});

<span class="hljs-comment">// UTILITY FUNCTIONS</span>
const renderFundRaisingPromo <span class="hljs-operator">=</span> (entries) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
  const [entry] <span class="hljs-operator">=</span> entries;

  <span class="hljs-keyword">if</span> (entry.intersectionRatio <span class="hljs-operator">></span><span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.4</span>) {
    fundRaisingPromoVisible <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    console.log(<span class="hljs-string">'Fund-raising promo is visible'</span>);
  } <span class="hljs-keyword">else</span> {
    fundRaisingPromoVisible <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
    console.log(<span class="hljs-string">'Fund-raising promo is not visible'</span>);
  }
};

const renderDonationPromo <span class="hljs-operator">=</span> (entries) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
  const [entry] <span class="hljs-operator">=</span> entries;

  <span class="hljs-keyword">if</span> (entry.intersectionRatio <span class="hljs-operator">></span><span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.4</span>) {
    donationPromoVisible <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    console.log(<span class="hljs-string">'Donation promo is visible'</span>);
  } <span class="hljs-keyword">else</span> {
    donationPromoVisible <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
    console.log(<span class="hljs-string">'Donation promo is not visible'</span>);
  }
};
</code></pre><p>In svelte you can use <code>bind:this</code> on an HTML element to have a reference to this element in the JavaScript code. That is what’s done for the <code>fundRaisingContainer</code> and <code>donationContainer</code> variables. The markup for these elements looks as follows:</p><pre data-type="codeBlock" text="// src/routes/+page.svelte

&lt;section bind:this={fundRaisingContainer}&gt;
  ...
&lt;/section&gt;
&lt;section bind:this={donationContainer}&gt;
  ...
&lt;/section&gt;
"><code><span class="hljs-comment">// src/routes/+page.svelte</span>

<span class="hljs-operator">&#x3C;</span>section bind:<span class="hljs-built_in">this</span><span class="hljs-operator">=</span>{fundRaisingContainer}<span class="hljs-operator">></span>
  ...
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>section<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>section bind:<span class="hljs-built_in">this</span><span class="hljs-operator">=</span>{donationContainer}<span class="hljs-operator">></span>
  ...
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>section<span class="hljs-operator">></span>
</code></pre><p>Continuing with the setup of the observers, it can be seen that the observer options are defined and then the observers are created using the callbacks and the observer options as seen below:</p><pre data-type="codeBlock" text="const fundRaisingPromoObserver = new IntersectionObserver(
    renderFundRaisingPromo,
    observerOptions
 );

 const donationPromoObserver = new IntersectionObserver(
   renderDonationPromo, 
   observerOptions
 );
"><code>const <span class="hljs-attr">fundRaisingPromoObserver</span> = new IntersectionObserver(
    renderFundRaisingPromo,
    observerOptions
 )<span class="hljs-comment">;</span>

 const <span class="hljs-attr">donationPromoObserver</span> = new IntersectionObserver(
   renderDonationPromo, 
   observerOptions
 )<span class="hljs-comment">;</span>
</code></pre><p>Next, the observers need to be assigned a target element to observe:</p><pre data-type="codeBlock" text="fundRaisingPromoObserver.observe(fundRaisingContainer);
donationPromoObserver.observe(donationContainer);
"><code>fundRaisingPromoObserver.observe(fundRaisingContainer);
donationPromoObserver.observe(donationContainer);
</code></pre><p>Finally, the observers should be cleaned up and removed when leaving the homepage. The <code>onMount</code> lifecycle hook will run any function returned from the lifecycle hook. Therefore, the observer cleanup code can be written here:</p><pre data-type="codeBlock" text="return () =&gt; {
  // Halt observing html elements
  fundRaisingPromoObserver.unobserve(fundRaisingContainer);
  donationPromoObserver.unobserve(donationContainer);

  // Close the intersection observers
  fundRaisingPromoObserver.disconnect();
  donationPromoObserver.disconnect();
};
"><code><span class="hljs-keyword">return</span> () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
  <span class="hljs-comment">// Halt observing html elements</span>
  fundRaisingPromoObserver.unobserve(fundRaisingContainer);
  donationPromoObserver.unobserve(donationContainer);

  <span class="hljs-comment">// Close the intersection observers</span>
  fundRaisingPromoObserver.disconnect();
  donationPromoObserver.disconnect();
};
</code></pre><p>As can be observed from the console, the observers are functioning correctly. They fire the callbacks and update the state variables when the scroll changes and content visibility is updated. The final step is to create animations for these observers to trigger to complete the homepage.</p><h3 id="h-animations-on-scroll" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Animations on scroll</h3><p>To implement this functionality, CSS keyframes and animations can be used:</p><pre data-type="codeBlock" text="// src/lib/animationsAndTransitions/fade.scss

@keyframes fadeIn {
  0% {
    opacity: 0;
  }

  100% {
    opacity: 1;
  }
}

@keyframes fadeOut {
  0% {
    opacity: 1;
  }

  100% {
    opacity: 0;
  }
}

.fade-in {
  animation: fadeIn 0.75s ease-in forwards;
}

.fade-out {
  animation: fadeOut 0.5s ease-in forwards;
}
"><code>// <span class="hljs-attribute">src</span>/lib/animationsAndTransitions/fade<span class="hljs-selector-class">.scss</span>

<span class="hljs-keyword">@keyframes</span> fadeIn {
  <span class="hljs-number">0%</span> {
    <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
  }

  <span class="hljs-number">100%</span> {
    <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>;
  }
}

<span class="hljs-keyword">@keyframes</span> fadeOut {
  <span class="hljs-number">0%</span> {
    <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>;
  }

  <span class="hljs-number">100%</span> {
    <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
  }
}

<span class="hljs-selector-class">.fade-in</span> {
  <span class="hljs-attribute">animation</span>: fadeIn <span class="hljs-number">0.75s</span> ease-in forwards;
}

<span class="hljs-selector-class">.fade-out</span> {
  <span class="hljs-attribute">animation</span>: fadeOut <span class="hljs-number">0.5s</span> ease-in forwards;
}
</code></pre><p>This file was added to the previously created <code>animationsAndTransitions</code> directory. The <code>@keyframes</code> allows animations to be generated by specifying how the style of an element should change over time. Here we change <code>opacity</code> from 0 to 1 or vice versa. This creates the animation of a fade-in/out. Once these changes are defined, they can be applied using the <code>animation</code> property. The <code>fade-in</code> and <code>fade-out</code> classes will apply the keyframes to HTML elements that possess the class. The <code>animation</code> property includes several parameters. Examining the <code>fade-in</code> class:</p><ul><li><p>First is, the name of the <code>@keyframes</code> animation to use. <code>fadeIn</code> in this case.</p></li><li><p>Second is, the amount of time to complete one cycle of the animation. This is 0.75s above.</p></li><li><p>Next is <code>animation-timing-function</code> , this defines the rate of change of the animation. E.g. linear, ease-in, etc. <code>ease-in</code> is used to start the animation slowly and then gradually speed up.</p></li><li><p>Finally, <code>animation-direction</code> controls whether the animation plays forward, backward or alternates. <code>forward</code> was used above to play the animation from 0% to 100% and retain the final style of the element.</p></li></ul><p>The complete list of parameters can be found <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.w3schools.com/cssref/css3_pr_animation.php">here</a>.</p><p>Next, we need to import these animations into the <code>main.scss</code> file using the <code>use</code> keyword to ensure that these new animation classes are available in all the Svelte components.</p><pre data-type="codeBlock" text="// src/lib/styles/main.scss

@use &apos;buttons&apos;;
@use &apos;@fontsource-variable/ubuntu-sans&apos;;
@use &apos;../animationsAndTransitions/fade.scss&apos;;

...
"><code><span class="hljs-comment">// src/lib/styles/main.scss</span>

<span class="hljs-variable">@use</span> <span class="hljs-string">'buttons'</span>;
<span class="hljs-variable">@use</span> <span class="hljs-string">'@fontsource-variable/ubuntu-sans'</span>;
<span class="hljs-variable">@use</span> <span class="hljs-string">'../animationsAndTransitions/fade.scss'</span>;

...
</code></pre><p>Next, these classes can be applied to the new content in <code>+page.svelte</code>:</p><pre data-type="codeBlock" text="// src/routes/+page.svelte

...

&lt;section
  bind:this={fundRaisingContainer}
  class={`promo-section ${fundRaisingPromoVisible ? &apos;fade-in&apos; : &apos;fade-out&apos;}`}
&gt;
  ...		
&lt;/section&gt;
&lt;section
  class={`promo-section ${donationPromoVisible ? &apos;fade-in&apos; : &apos;fade-out&apos;}`}
  bind:this={donationContainer}
&gt;
  ...
&lt;/section&gt;
"><code><span class="hljs-comment">// src/routes/+page.svelte</span>

...

<span class="hljs-operator">&#x3C;</span>section
  bind:<span class="hljs-built_in">this</span><span class="hljs-operator">=</span>{fundRaisingContainer}
  class<span class="hljs-operator">=</span>{`promo<span class="hljs-operator">-</span>section ${fundRaisingPromoVisible ? <span class="hljs-string">'fade-in'</span> : <span class="hljs-string">'fade-out'</span>}`}
<span class="hljs-operator">></span>
  ...		
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>section<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>section
  class<span class="hljs-operator">=</span>{`promo<span class="hljs-operator">-</span>section ${donationPromoVisible ? <span class="hljs-string">'fade-in'</span> : <span class="hljs-string">'fade-out'</span>}`}
  bind:<span class="hljs-built_in">this</span><span class="hljs-operator">=</span>{donationContainer}
<span class="hljs-operator">></span>
  ...
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>section<span class="hljs-operator">></span>
</code></pre><p>And voilà a simple yet modern and interactive landing page.</p><p>Please refer to the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/MGM103/svelte5-onchain-app/tree/main/frontend-part-1">GitHub repo</a> for the full source code of the completed part 1 code.</p><h3 id="h-transitions-vs-css-animations-with-keyframes" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Transitions vs CSS animations with keyframes</h3><p>You may be wondering why CSS animations were used on the content sections as opposed to svelte transitions, which were used on the landing section. This is a fair point to wonder and I want to explain my rationale behind this choice.</p><p>Firstly, recall that svelte transitions can only be applied to an element when it is entering or leaving the DOM as a result of a state change. This is trivial to set up, there are already visibility state variables for each of the content sections. The content within the <code>&lt;section&gt;</code> tags could be wrapped in a conditional template <code>{#if isVisible} … {/if}</code> and then transitions could be applied to the content. However, the height of the content sections (<code>.promo-section</code>) is: <code>max-content</code>. This presents an issue with the intersection observer. If the content is not yet rendered because it is not visible, then the section has no content and its height is zero. This means the observer can never observe it intersecting with the viewport, as having a height of zero effectively makes the element invisible. Therefore, the visibility state variable will never be updated and the content will never show.</p><p>Of course, it is possible to specify a <code>min-height</code> to be portion of the <code>vh</code> as done with the landing section, however, I did not wish to do that. I wanted the height of the container to accommodate only the amount of space required. This is why I used keyframes and animations. The intersection observer could update state variables which would modify the class applied to the content creating the same effect. However, this approach ensures there’s no popping or excessive space taken for content on the page, creating the best user experience.</p><h2 id="h-wrapping-up" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Wrapping up</h2><p>I would like to reiterate that all code from this section can be found <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/MGM103/svelte5-onchain-app/tree/main/frontend-part-1">here</a>. This article explored and discussed a number of topics:</p><ul><li><p>SvelteKit Project Set up</p></li><li><p>The UI Deisgn Philosophy</p></li><li><p>SvelteKit Route Files</p></li><li><p>Svelte Transitions</p></li><li><p>Intersection Observers</p></li><li><p>CSS Keyframes and Animations</p></li></ul><p>At the conclusion of this article, a modern homepage has been built and the foundation has been laid to create other components and pages, as well as implementing features such as toggling between light and dark themes.</p><p>The next instalment will explore these topics and should be out shortly.</p><p>If you found this article helpful please subscribe for notifications about future articles and mint to show you support!</p><div data-type="subscribeButton" class="center-contents"><a class="email-subscribe-button" href="null">Subscribe</a></div><p>See you in the next one👋</p>]]></content:encoded>
            <author>0xnelli@newsletter.paragraph.com (0xNelli)</author>
        </item>
    </channel>
</rss>