<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: ouzz</title>
    <description>The latest articles on Forem by ouzz (@ouzhou).</description>
    <link>https://forem.com/ouzhou</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F73732%2Fe4274e16-e7a9-433f-9d79-3b927b842437.jpeg</url>
      <title>Forem: ouzz</title>
      <link>https://forem.com/ouzhou</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ouzhou"/>
    <language>en</language>
    <item>
      <title>I Built a Liquid Glass Lens Effect in React Canvas</title>
      <dc:creator>ouzz</dc:creator>
      <pubDate>Wed, 15 Apr 2026 10:24:48 +0000</pubDate>
      <link>https://forem.com/ouzhou/i-built-a-liquid-glass-lens-effect-in-react-canvas-1hi0</link>
      <guid>https://forem.com/ouzhou/i-built-a-liquid-glass-lens-effect-in-react-canvas-1hi0</guid>
      <description>&lt;h2&gt;
  
  
  I Built a Liquid Glass Lens Effect in React Canvas
&lt;/h2&gt;

&lt;p&gt;I recently implemented a "liquid glass lens" post-processing effect in &lt;code&gt;react-canvas&lt;/code&gt;, with three goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A clear magnification feel at the lens center&lt;/li&gt;
&lt;li&gt;  Strong glass-like distortion plus subtle chromatic dispersion near the rim&lt;/li&gt;
&lt;li&gt;  No second rendering stack, by fully reusing the existing CanvasKit pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article documents the implementation approach, key ideas, and the pitfalls I ran into.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project and Live Demo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  GitHub repository: &lt;a href="https://github.com/ouzhou/react-canvas" rel="noopener noreferrer"&gt;https://github.com/ouzhou/react-canvas&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  Live demo: &lt;a href="https://react-canvas-design.vercel.app/#/devto" rel="noopener noreferrer"&gt;https://react-canvas-design.vercel.app/#/devto&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8sw8s587m9q39d137h2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8sw8s587m9q39d137h2.png" alt="demo" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Post-Process Instead of an Overlay Layer
&lt;/h2&gt;

&lt;p&gt;At first, the most intuitive approach seemed to be adding another canvas or WebGL layer dedicated to the lens effect.\&lt;br&gt;
But that introduces two major issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; High synchronization cost between two render outputs (scrolling, zooming, and camera state must stay aligned)&lt;/li&gt;
&lt;li&gt; Interaction picking can conflict with visual composition&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So I chose a different route: &lt;strong&gt;full-screen post-processing within the same CanvasKit pipeline&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The flow is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Render the full scene to an offscreen surface&lt;/li&gt;
&lt;li&gt; Apply a SkSL &lt;code&gt;RuntimeEffect&lt;/code&gt; to the offscreen image&lt;/li&gt;
&lt;li&gt; Draw the processed result to the main canvas in one pass&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Benefits of this design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Scene logic is fully reused, with no need for a dual render tree&lt;/li&gt;
&lt;li&gt;  Effect control is centralized in shader code and uniforms&lt;/li&gt;
&lt;li&gt;  High compatibility with the existing rendering architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Breaking Down the Lens Effect
&lt;/h2&gt;

&lt;p&gt;The current liquid glass lens consists of four parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Center magnification&lt;/strong&gt;: clear enlargement inside the lens&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Rim distortion&lt;/strong&gt;: distortion concentrated near the outer ring&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Chromatic dispersion (RGB offset)&lt;/strong&gt;: subtle color fringing at the edge to enhance the glass feel&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Rim highlight + shadow&lt;/strong&gt;: gives the lens a tangible physical presence&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One key optimization I focused on:\&lt;br&gt;
&lt;strong&gt;the center should magnify but not distort; distortion should be concentrated in the outer ~20% ring.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The implementation introduces a normalized radius &lt;code&gt;t&lt;/code&gt;, then uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;rim = smoothstep(0.8, 1.0, t)&lt;/code&gt; as the edge weight&lt;/li&gt;
&lt;li&gt;  Near-zero distortion weight at the center (&lt;code&gt;t &amp;lt; 0.8&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  Gradually increasing distortion and dispersion as &lt;code&gt;t -&amp;gt; 1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visually, this feels much closer to edge refraction of a real lens, instead of making the entire area wobble.&lt;/p&gt;

&lt;h2&gt;
  
  
  Animation and Performance: From Always Repainting to On-Demand Repainting
&lt;/h2&gt;

&lt;p&gt;The lens is a pure uniform-driven post effect. Forcing a repaint every frame is simple, but unnecessarily expensive.\&lt;br&gt;
I switched to an on-demand strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  On pointer movement, actively wake rendering via &lt;code&gt;requestCanvasRepaint(canvas)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;shouldContinueRepaint&lt;/code&gt; continues only while the lens is still chasing its target point&lt;/li&gt;
&lt;li&gt;  Continuous repainting stops automatically once the lens settles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This change significantly improved perceived performance: smooth while moving, no idle spinning when stopped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coordinate and Boundary Handling
&lt;/h2&gt;

&lt;p&gt;On the web, coordinate mapping and edge sampling are the easiest places to make mistakes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Convert pointer coordinates using &lt;code&gt;getBoundingClientRect()&lt;/code&gt; plus backing-store scale&lt;/li&gt;
&lt;li&gt;  Clamp shader UV sampling with &lt;code&gt;clamp(0..1)&lt;/code&gt; to avoid black edges or out-of-bounds artifacts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These details look minor, but they directly affect the final polish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; In visual effects, deciding &lt;strong&gt;where not to apply effects&lt;/strong&gt; is often as important as deciding what to apply&lt;/li&gt;
&lt;li&gt; For lens effects, a stable center plus expressive edges looks more natural than full-area distortion&lt;/li&gt;
&lt;li&gt; Once the architecture is right (a unified post-process pipeline), iterative tuning becomes much cheaper&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're building glass, depth-of-field, or color-grading effects in Canvas/Skia scenes, I strongly recommend starting with the "offscreen scene + &lt;code&gt;RuntimeEffect&lt;/code&gt;" approach. It scales much better for future extensions.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>react</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Building the DEV Community Homepage with Pure Canvas</title>
      <dc:creator>ouzz</dc:creator>
      <pubDate>Tue, 14 Apr 2026 08:35:24 +0000</pubDate>
      <link>https://forem.com/ouzhou/building-the-dev-community-homepage-with-pure-canvas-8d0</link>
      <guid>https://forem.com/ouzhou/building-the-dev-community-homepage-with-pure-canvas-8d0</guid>
      <description>&lt;h2&gt;
  
  
  Building the DEV Community Homepage with Pure Canvas
&lt;/h2&gt;

&lt;p&gt;In modern frontend development, we are accustomed to building user interfaces with HTML and CSS. But have you ever wondered what it would be like to completely abandon the DOM tree and use pure Canvas to draw a complex modern web page (like the DEV Community homepage)?&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;react-canvas&lt;/code&gt; project, we took on a hardcore challenge: &lt;strong&gt;building the DEV Community homepage from scratch using a custom React renderer, powered by Skia (CanvasKit) and the Yoga layout engine.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;strong&gt;Live Demo&lt;/strong&gt;: &lt;a href="https://react-canvas-design.vercel.app/#/devto" rel="noopener noreferrer"&gt;https://react-canvas-design.vercel.app/#/devto&lt;/a&gt;&lt;br&gt;&lt;br&gt;
💻 &lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/ouzhou/react-canvas" rel="noopener noreferrer"&gt;https://github.com/ouzhou/react-canvas&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  preview
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfy28joopy0p1xbe5gyv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfy28joopy0p1xbe5gyv.png" alt="preview" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Unveiling the Tech Stack
&lt;/h2&gt;

&lt;p&gt;To achieve this goal, we couldn't use the standard &lt;code&gt;react-dom&lt;/code&gt;. Our underlying infrastructure includes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;CanvasKit (Skia WebAssembly)&lt;/strong&gt;: Serves as the underlying 2D graphics rendering engine, responsible for drawing all rectangles, text, images, and SVG paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yoga Layout&lt;/strong&gt;: A cross-platform Flexbox layout engine open-sourced by Facebook. Since Canvas itself has no concept of layout, we use Yoga to calculate the coordinates and dimensions of each element.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@react-canvas/react-v2&lt;/strong&gt;: Our custom-built React renderer that maps the React component tree to the underlying rendering nodes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Core Implementation Concepts
&lt;/h2&gt;

&lt;p&gt;In the world of pure Canvas, there are no &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;, or &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags. Everything is a custom node.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Basic Component Mapping
&lt;/h3&gt;

&lt;p&gt;We replaced traditional HTML tags with the basic components provided by &lt;code&gt;react-canvas&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; -&amp;gt; &lt;code&gt;&amp;lt;View&amp;gt;&lt;/code&gt;: Acts as the basic container, supporting Flexbox layout.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; / &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; -&amp;gt; &lt;code&gt;&amp;lt;Text&amp;gt;&lt;/code&gt;: Used for text rendering, calling Skia's Paragraph API under the hood.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; -&amp;gt; &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt;: Used for rendering network images.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; -&amp;gt; &lt;code&gt;&amp;lt;SvgPath&amp;gt;&lt;/code&gt;: Used for rendering vector icons.&lt;/li&gt;
&lt;li&gt;Scrollable areas -&amp;gt; &lt;code&gt;&amp;lt;ScrollView&amp;gt;&lt;/code&gt;: Since Canvas has no native scrollbars, we have to handle scroll events and viewport clipping ourselves.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Canvas Initialization
&lt;/h3&gt;

&lt;p&gt;We use &lt;code&gt;CanvasProvider&lt;/code&gt; at the outermost layer to initialize the runtime and load the necessary fonts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CanvasProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;View&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@react-canvas/react-v2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;localParagraphFontUrl&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../assets/NotoSansSC-Regular.otf?url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CanvasProvider&lt;/span&gt; &lt;span class="na"&gt;initOptions&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;defaultParagraphFontUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;localParagraphFontUrl&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;isReady&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Canvas&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;vw&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;vh&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;paragraphFontProvider&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paragraphFontProvider&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;defaultParagraphFontFamily&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultParagraphFontFamily&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Page Content */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CanvasProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Flexbox Layout &amp;amp; Styling
&lt;/h3&gt;

&lt;p&gt;Thanks to Yoga, we can use Flexbox layout just like in React Native. All styles are inline JS objects rather than CSS classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example layout for the DEV top navigation bar&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#ffffff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;flexDirection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;space-between&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paddingLeft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paddingRight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Logo and Navigation Items */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Interactive States (Hover)
&lt;/h3&gt;

&lt;p&gt;In the DOM, we typically use the &lt;code&gt;:hover&lt;/code&gt; pseudo-class to handle mouse hover states. In &lt;code&gt;react-canvas&lt;/code&gt;, the &lt;code&gt;style&lt;/code&gt; property supports passing a function that receives the current interaction state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;hovered&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hovered&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rgba(59, 73, 223, 0.1)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transparent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Change background on hover&lt;/span&gt;
    &lt;span class="na"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pointer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;hovered&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hovered&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#3b49df&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#404040&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    Home
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Drawing Details: Borders and Dividers
&lt;/h3&gt;

&lt;p&gt;In traditional CSS, we can easily write &lt;code&gt;border-bottom: 1px solid #e5e5e5&lt;/code&gt;. However, in our current custom renderer, support for single-sided borders is still being refined.&lt;/p&gt;

&lt;p&gt;To draw perfect 1px dividers in Canvas, we use absolutely positioned &lt;code&gt;&amp;lt;View&amp;gt;&lt;/code&gt; elements to simulate them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simulating border-bottom&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;absolute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#e5e5e5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Final Result
&lt;/h2&gt;

&lt;p&gt;By combining these basic capabilities, we successfully replicated the complex layout of the DEV Community homepage 1:1, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A fixed top navigation bar (with search box, icons, and the "Create Post" button).&lt;/li&gt;
&lt;li&gt;A left sidebar with navigation links and social icons.&lt;/li&gt;
&lt;li&gt;The main article feed (featuring the "What's on your mind?" input, tabs, and detailed article cards with nested comments).&lt;/li&gt;
&lt;li&gt;A right sidebar with "Active discussions" and trending tags.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All rendering is done entirely within a single &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; tag!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building complex Web UIs with pure Canvas is a fascinating exploration. While it loses the accessibility (A11y), SEO, and native text selection capabilities provided by the DOM, it offers ultimate rendering control and cross-platform consistency (the exact same code can easily be ported to native mobile apps or even desktop environments).&lt;/p&gt;

&lt;p&gt;This is the core appeal of technologies like Flutter and React Native Skia. Through &lt;code&gt;react-canvas&lt;/code&gt;, we've brought this "pixel-perfect control" experience to the Web.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>react</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
