<?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: elvis kahoro</title>
    <description>The latest articles on Forem by elvis kahoro (@elviskahoro).</description>
    <link>https://forem.com/elviskahoro</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%2F755174%2Fa5d71948-0154-4232-94fc-9fa48ab1a6c1.jpeg</url>
      <title>Forem: elvis kahoro</title>
      <link>https://forem.com/elviskahoro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/elviskahoro"/>
    <language>en</language>
    <item>
      <title>Designing a Pure Python Web Framework</title>
      <dc:creator>elvis kahoro</dc:creator>
      <pubDate>Wed, 18 Sep 2024 16:33:45 +0000</pubDate>
      <link>https://forem.com/reflex-dev/designing-a-pure-python-web-framework-110g</link>
      <guid>https://forem.com/reflex-dev/designing-a-pure-python-web-framework-110g</guid>
      <description>&lt;p&gt;Web development is one of the most popular use cases for programming. Python is one of the most popular programming languages in the world. So why can't we build web apps in Python?&lt;/p&gt;

&lt;p&gt;Making a UI should be simple, but even you have great engineers on your team, the overhead of learning a new language and tools was a huge barrier. Often making a UI could be harder than the actual work one is doing!&lt;/p&gt;

&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;p&gt;Under the hood, Reflex apps compile down to a &lt;a href="https://react.dev" rel="noopener noreferrer"&gt;React&lt;/a&gt; frontend app and a &lt;a href="https://github.com/tiangolo/fastapi" rel="noopener noreferrer"&gt;FastAPI&lt;/a&gt; backend app. Only the UI is compiled to Javascript; all the app logic and state management stays in Python and is run on the server. Reflex uses &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" rel="noopener noreferrer"&gt;WebSockets&lt;/a&gt; to send events from the frontend to the backend, and to send state updates from the backend to the frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Existing Python Solutions
&lt;/h2&gt;

&lt;p&gt;There were a few ways already to build apps in Python, but none of them fit our needs.&lt;/p&gt;

&lt;p&gt;On the one hand, there are frameworks like &lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt; and &lt;a href="https://flask.palletsprojects.com/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt; that are great for building production-grade web apps. But they only handle the backend - you still need to use JavaScript and a frontend framework, as well as writing a lot of boilerplate code to connect the frontend and backend.&lt;/p&gt;

&lt;p&gt;On the other hand, pure Python libraries like &lt;a href="https://dash.plotly.com/" rel="noopener noreferrer"&gt;Dash&lt;/a&gt; and &lt;a href="https://streamlit.io/" rel="noopener noreferrer"&gt;Streamlit&lt;/a&gt; can be great for small projects, but they are limited to a specific use case and don't have the features and performance to build a full web app. As your app grows in features and complexity, you may find yourself hitting the limits of the framework, at which point you either have to limit your idea to fit the framework, or scrap your project and rebuild it using a "real web framework".&lt;/p&gt;

&lt;p&gt;We want to bridge this gap by creating a framework that is easy and intuitive to get started with, while remaining flexible and powerful to support any app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goals of Reflex
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pure Python&lt;/strong&gt;: Use one language for everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to get started&lt;/strong&gt;: Build your ideas easily without needing web development experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full flexibility&lt;/strong&gt;: Web apps should match the customizability and performance of traditional web frameworks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batteries included&lt;/strong&gt;: Handle the full-stack from the frontend, to the backend, to deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's dive into how we built Reflex to meet these goals.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Reflex Architecture
&lt;/h2&gt;

&lt;p&gt;Full-stack web apps are made up of a frontend and a backend. The frontend is the user interface, and is served as a web page that runs on the user's browser. The backend handles the logic and state management (such as databases and APIs), and is run on a server.&lt;/p&gt;

&lt;p&gt;In traditional web development, these are usually two separate apps, and are often written in different frameworks or languages. For example, you may combine a Flask backend with a React frontend. With this approach, you have to maintain two separate apps and end up writing a lot of boilerplate code to connect the frontend and backend.&lt;/p&gt;

&lt;p&gt;We want to simplify this process in Reflex by defining both the frontend and backend in a single codebase, while using Python for everything. Developers should only worry about their app's logic and not about the low-level implementation details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F27up73wkqilut02f033x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F27up73wkqilut02f033x.png" alt="Image description" width="800" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend
&lt;/h2&gt;

&lt;p&gt;We want Reflex apps to look and feel like a traditional web app to the end user, while still being easy to build and maintain for the developer. To do this, we built on top of mature and popular web technologies.&lt;/p&gt;

&lt;p&gt;When you &lt;code&gt;reflex run&lt;/code&gt; your app, Reflex compiles the frontend down to a single-page &lt;a href="https://nextjs.org" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; app and serves it on a port (by default &lt;code&gt;3000&lt;/code&gt;) that you can access in your browser.&lt;/p&gt;

&lt;p&gt;The frontend's job is to reflect the app's state, and send events to the backend when the user interacts with the UI. No actual logic is run on the frontend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Components
&lt;/h3&gt;

&lt;p&gt;Reflex frontends are built using components that can be composed together to create complex UIs. Instead of using a templating language that mixes HTML and Python, we just use Python functions to define the UI.&lt;/p&gt;

&lt;p&gt;Under the hood, components compile down to React components.&lt;/p&gt;

&lt;p&gt;Many of our core components are based on &lt;a href="https://radix-ui.com/" rel="noopener noreferrer"&gt;Radix&lt;/a&gt;, a popular React component library. We also have many other components for graphing, datatables, and more.&lt;/p&gt;

&lt;p&gt;We chose React because it is a popular library with a huge ecosystem. Our goal isn't to recreate the web ecosystem, but to make it accessible to Python developers.&lt;/p&gt;

&lt;p&gt;This also lets our users bring their own components if we don't have a component they need. Users can wrap their own React components and then publish them for others to use. Over time we will build out our third party component ecosystem so that users can easily find and use components that others have built.&lt;/p&gt;

&lt;h3&gt;
  
  
  Styling
&lt;/h3&gt;

&lt;p&gt;We wanted to make sure Reflex apps look good out of the box, while still giving developers full control over the appearance of their app.&lt;/p&gt;

&lt;p&gt;We have a core theming system that lets you set high level styling options such as dark mode and accent color throughout your app to give it a unified look and feel.&lt;/p&gt;

&lt;p&gt;Beyond this, Reflex components can be styled using the full power of CSS. We leverage the &lt;a href="https://emotion.sh/docs/introduction" rel="noopener noreferrer"&gt;Emotion&lt;/a&gt; library to allow "CSS-in-Python" styling, so you can pass any CSS prop as a keyword argument to a component. This includes responsive props by passing a list of values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backend
&lt;/h2&gt;

&lt;p&gt;In Reflex only the frontend compiles to Javascript and runs on the user's browser, while all the state and logic stays in Python and is run on the server. When you &lt;code&gt;reflex run&lt;/code&gt;, we start a FastAPI server (by default on port &lt;code&gt;8000&lt;/code&gt;) that the frontend connects to through a websocket.&lt;/p&gt;

&lt;p&gt;All the state and logic are defined within a &lt;code&gt;State&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;The state is made up of &lt;strong&gt;vars&lt;/strong&gt; and &lt;strong&gt;event handlers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Vars are any values in your app that can change over time. They are defined as class attributes on your &lt;code&gt;State&lt;/code&gt; class, and may be any Python type that can be serialized to JSON.&lt;/p&gt;

&lt;p&gt;Event handlers are methods in your &lt;code&gt;State&lt;/code&gt; class that are called when the user interacts with the UI. They are the only way that we can modify the vars in Reflex, and can be called in response to user actions, such as clicking a button or typing in a text box.&lt;/p&gt;

&lt;p&gt;Since event handlers are run on the backend, you can use any Python library within them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8gys5m0zsd6a657414z7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8gys5m0zsd6a657414z7.png" alt="Image description" width="800" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Event Processing
&lt;/h2&gt;

&lt;p&gt;Normally when writing web apps, you have to write a lot of boilerplate code to connect the frontend and backend. With Reflex, you don't have to worry about that - we handle the communication between the frontend and backend for you. Developers just have to write their event handler logic, and when the vars are updated the UI is automatically updated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Event Triggers
&lt;/h3&gt;

&lt;p&gt;The user can interact with the UI in many ways, such as clicking a button, typing in a text box, or hovering over an element. In Reflex, we call these &lt;strong&gt;event triggers&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Event Queue
&lt;/h3&gt;

&lt;p&gt;On the frontend, we maintain an event queue of all pending events. An event consists of three major pieces of data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;client token&lt;/strong&gt;: Each client (browser tab) has a unique token to identify it. This let's the backend know which state to update.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;event handler&lt;/strong&gt;: The event handler to run on the state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;arguments&lt;/strong&gt;: The arguments to pass to the event handler.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When an event is triggered, it is added to the queue.&lt;br&gt;
We have a &lt;code&gt;processing&lt;/code&gt; flag to make sure only one event is processed at a time. This ensures that the state is always consistent and there aren't any race conditions with two event handlers modifying the state at the same time. There are exceptions to this, such as background events which allow you to run events in the background without blocking the UI.&lt;/p&gt;

&lt;p&gt;Once the event is ready to be processed, it is sent to the backend through a WebSocket connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  State Manager
&lt;/h3&gt;

&lt;p&gt;Once the event is received, it is processed on the backend.&lt;/p&gt;

&lt;p&gt;Reflex uses a &lt;strong&gt;state manager&lt;/strong&gt; which maintains a mapping between client tokens and their state. By default, the state manager is just an in-memory dictionary, but it can be extended to use a database or cache. In production we use Redis as our state manager.&lt;/p&gt;

&lt;h3&gt;
  
  
  Event Handling
&lt;/h3&gt;

&lt;p&gt;Once we have the user's state, the next step is to run the event handler with the arguments.&lt;/p&gt;

&lt;h3&gt;
  
  
  State Updates
&lt;/h3&gt;

&lt;p&gt;Every time an event handler returns (or yields), we save the state in the state manager and send the &lt;strong&gt;state updates&lt;/strong&gt; to the frontend to update the UI.&lt;/p&gt;

&lt;p&gt;To maintain performance as your state grows, internally Reflex keeps track of vars that were updated during the event handler (&lt;strong&gt;dirty vars&lt;/strong&gt;).&lt;br&gt;
When the event handler is done processing, we find all the dirty vars and create a state update to send to the frontend.&lt;/p&gt;

&lt;p&gt;We store the new state in our state manager, and then send the state update to the frontend.&lt;br&gt;
The frontend then updates the UI to reflect the new state.&lt;/p&gt;

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

&lt;p&gt;I hope this provides a good overview of how Reflex works under the hood. We will have more posts coming out to share how we made Reflex scalable and performant through features such as state sharding and compiler optimizations.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>react</category>
      <category>webapp</category>
    </item>
    <item>
      <title>How we designed themes for the terminal - a peek into our process</title>
      <dc:creator>elvis kahoro</dc:creator>
      <pubDate>Thu, 17 Mar 2022 05:16:23 +0000</pubDate>
      <link>https://forem.com/warpdotdev/how-we-designed-themes-for-the-terminal-a-peek-into-our-process-2kn4</link>
      <guid>https://forem.com/warpdotdev/how-we-designed-themes-for-the-terminal-a-peek-into-our-process-2kn4</guid>
      <description>&lt;p&gt;It’s no secret that people love to customize their UIs. With the rampant adoption of light mode and dark mode and the long history of themes in terminals and IDEs, the power to make your UI fit your functionality or aesthetic needs is huge. Here at Warp we knew how important it is to create a great theming system for our users. Some key goals were to ensure compatibility with existing themes, easy customization and sharing.&lt;/p&gt;

&lt;p&gt;Let’s start with how themes work in existing terminals and what we wanted to improve.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Themes only control the text display, yet the rest of the UI remains the same. We want to deliver a more cohesive and immersive experience.&lt;/li&gt;
&lt;li&gt;Themes are hard to control, you have to turn a ton of individual “knobs” in order to get something that looks good. It’s not easy to get what you want and add your personal preference. We want to make customization simple, flexible and quick.&lt;/li&gt;
&lt;li&gt;Themes are hard to share. Importing and exporting themes are different and can require several steps. We want to make it a one click simple experience to import and share.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  The foundation
&lt;/h1&gt;

&lt;p&gt;To ensure warp could be compatible with all other existing themes we started with the standard foundation of 16 ANSI colors. Using these standards as our foundation allows practically every terminal theme to be used in warp.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg75wv85y7tfbrzropdat.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg75wv85y7tfbrzropdat.png" alt="16 ANSI Colors" width="325" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We wanted to take it one step further and have the theme extend to the whole UI. Since we custom built Warp’s UI this allows us to have full control of the look and feel.&lt;/p&gt;

&lt;p&gt;The first step of that was to consider the UI accent areas like the tab indicator and block selection. We want these areas to pop and gain your attention while also adding some visual flair. We needed to add another color attribute which we naturally called the accent color.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcevl8ug0crphzlmrj2yf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcevl8ug0crphzlmrj2yf.png" alt="Warp Dark Theme" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being able to change the accent color gave themes a wider range of customization just from one color change without changing the look and functionality of the core theme.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f7Jx4CCA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1646741107395/bjEjFGQfr.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f7Jx4CCA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1646741107395/bjEjFGQfr.gif" alt="color change.gif" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  UI theming
&lt;/h2&gt;

&lt;p&gt;There is a bit more UI to cover within Warp like contextual menus, auto suggestion overlays, dialogs, command palette. The text coloring could easily be inherited from the theme and the accent color used for the same conditions, though we wanted the UI surface to stand out from the background. The last thing we wanted to do is to add yet another color attribute.&lt;/p&gt;

&lt;p&gt;Starting with dark and light themes we had to handle the UI layering in different ways. To achieve separation from the background for dark themes, we added a white overlay which aligns with the core text color, and for light themes the opposite, black overlay which also aligns with the core text color.&lt;/p&gt;

&lt;p&gt;We created a consistent style called “UI surface” which would be the background of all the overlay UI elements.&lt;/p&gt;

&lt;p&gt;It consists of the theme background color, the opposite overlay color and an outline.&lt;/p&gt;

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

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

&lt;p&gt;This basic system allows for a different UI surface leveraging the same color system. Seen here in dark and light mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feud6ioxg3zqvjbxqle0e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feud6ioxg3zqvjbxqle0e.png" alt="Warp Dark Mode" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe977xhorqgy5667nqlhu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe977xhorqgy5667nqlhu.png" alt="Warp Light Mode" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The really neat part is how this easily extends to existing themes like &lt;a href="https://github.com/dracula/dracula-theme" rel="noopener noreferrer"&gt;Dracula&lt;/a&gt; and &lt;a href="https://github.com/altercation/solarized" rel="noopener noreferrer"&gt;Solarized&lt;/a&gt;. For existing themes we convert, we will choose an accent color from the core 16 colors that we feel match the best. If you want to change it, no sweat, you can customize the accent color to any of the 16 theme colors or any custom color you like to add your personal preference to existing themes.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfbrexo1lkr9l7v2vcrz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfbrexo1lkr9l7v2vcrz.png" alt="Solarized Light" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Image backgrounds
&lt;/h2&gt;

&lt;p&gt;So with the standard theme color system and an added accent color attribute, we can easily customize the whole Warp UI. Yet we didn’t want to stop there, what about adding photos and advanced color treatment?&lt;/p&gt;

&lt;p&gt;We experimented with adding photo backgrounds with color themes that matched and loved the results.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddzye765i9fu7qfymqmq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddzye765i9fu7qfymqmq.png" alt="Leafy theme" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uddgxo14gwaiomb8ymv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uddgxo14gwaiomb8ymv.png" alt="Leafy theme with menus" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fynp1h5e3oh7qasqncoxi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fynp1h5e3oh7qasqncoxi.png" alt="Dark City image" width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvah8ym2vqpwks4kfnr31.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvah8ym2vqpwks4kfnr31.png" alt="Dark City theme" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbm0f8vxj3r65nyf1zhmo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbm0f8vxj3r65nyf1zhmo.png" alt="Dark City theme with menus" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We also added the ability to support gradients, which can drastically change the look of a theme and add additional depth and visual pizzaz.&lt;/p&gt;

&lt;p&gt;Dracula regular:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxyyiihkqd02o7t4hyjmn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxyyiihkqd02o7t4hyjmn.png" alt="Dracula regular" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dracula with gradients:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fno1ictsu18yet1hjulpz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fno1ictsu18yet1hjulpz.png" alt="Dracula with gradients" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Customization
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kz61xm78j1g9s4vs0f6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kz61xm78j1g9s4vs0f6.gif" alt="Image description" width="800" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want our user to have the power to make amazing themes, so we are working on a theme editor that makes customizing themes super simple. For lightweight use, you can start with our default dark or light theme and change the accent color. Choose one of many popular themes we have built into warp or import your favorite. If you want to go for even more customization, add a photo, make your accent color a gradient. With just a few customization you can radically change the way your terminal looks and feels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing
&lt;/h2&gt;

&lt;p&gt;Currently, there are ton of different sites and resources to get terminal themes, each of them with totally different ways to download themes, from a file download, github repo, etc.&lt;/p&gt;

&lt;p&gt;For Warp themes, we want to build a one stop shop for themes. One spot where you can browse, install and share themes all with one click. We also want individual themes to be easily shareable from user to user within the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check out themes now
&lt;/h2&gt;

&lt;p&gt;It’s been really exciting working on themes in Warp. Building a system that can accommodate existing themes, expanding on them within Warp and giving users more control to customize and share. We can’t wait to see what themes get created.&lt;/p&gt;

&lt;p&gt;You can check out the base theme experience in Warp now, where you can choose from a set of themes and &lt;a href="https://github.com/warpdotdev/themes/" rel="noopener noreferrer"&gt;customize via an external file&lt;/a&gt;. Shortly gradients and image support will be rolling out. Customization and sharing are down the road.&lt;/p&gt;

&lt;p&gt;We are all super excited to get all these theming features out, we have a bit of work ahead of us! Please let us know if there are any ideas that brew up, we would love your input!&lt;/p&gt;

&lt;p&gt;Request early access to our beta. We look forward to your input!&lt;/p&gt;

</description>
      <category>warp</category>
      <category>terminal</category>
      <category>product</category>
      <category>design</category>
    </item>
  </channel>
</rss>
