Skip to main content

Flowbite-Based Jinja Components

Project description

Flowbite-Based Jinja Components

A collection of Flowbite component developed as Jinja Layout Templates and Macros.

Getting Started for Flask

For a pre-configured starter repo both web and desktop app, goto: https://github.com/schaveyt/jinja-flowbite-web-desktop-demo

Otherwise start from scratch following the steps below:

  1. Add the jinja-flowbite package to your python environment

    • Option 1: Add it to your requirements.txt
    • Option 2: Add it to your pyproject.toml if you are using poetry
    • Option 3: Install to your global packages: pip install jinja-flowbite
  2. Register the jinja_flowbite package to the Flask runtime

    from flask import Flask
    import jinja2
    
    app = Flask(__name__,)
    app.jinja_env.auto_reload = True
    
    enhanced_loader = jinja2.ChoiceLoader([
        jinja2.PackageLoader("jinja_flowbite", ""),
        app.jinja_loader
    ])
    
    app.jinja_loader = template_loader
    
  3. Register the jinja-flowbite source file with Tailwind configurations

    NOTE: Below is a standard flowbite setup with the key addition of adding the jinja-flowbite package .jinja files to the content section.

    /** @type {import('tailwindcss').Config} */
    module.exports = {
    
    content: [
        ".your_app_folder/templates/**/*.{html,jinja}",
        "./src/static/js/**/*.js",
        "./node_modules/flowbite/**/*.js",
        "./venv/Lib/site-packages/jinja_flowbite/**/*.{html,jinja}",
    ],
    darkMode: 'class',
    theme: {
        extend: {
            colors: {
                primary: { "50": "#eff6ff", "100": "#dbeafe", "200": "#bfdbfe", "300": "#93c5fd", "400": "#60a5fa", "500": "#3b82f6", "600": "#2563eb", "700": "#1d4ed8", "800": "#1e40af", "900": "#1e3a8a", "950": "#172554" }
            },
            maxHeight: {
                'table-xl': '60rem',
            }
        },
        fontFamily: {
            'body': [
                'Inter',
                'ui-sans-serif',
                'system-ui',
                '-apple-system',
                'system-ui',
                'Segoe UI',
                'Roboto',
                'Helvetica Neue',
                'Arial',
                'Noto Sans',
                'sans-serif',
                'Apple Color Emoji',
                'Segoe UI Emoji',
                'Segoe UI Symbol',
                'Noto Color Emoji'
            ],
            'sans': [
                'Inter',
                'ui-sans-serif',
                'system-ui',
                '-apple-system',
                'system-ui',
                'Segoe UI',
                'Roboto',
                'Helvetica Neue',
                'Arial',
                'Noto Sans',
                'sans-serif',
                'Apple Color Emoji',
                'Segoe UI Emoji',
                'Segoe UI Symbol',
                'Noto Color Emoji'
            ],
            'mono': [
                'ui-monospace',
                'SFMono-Regular',
                'MesloLGL Nerd Font Mono', 
                'Cascadia Mono',
                'Courier New'
            ]
        }
    },
    plugins: [
        require('flowbite/plugin')
    ],
    }
    
  4. In your HTML template, import the Components and use as Jinja macros

    {% import "jinja_flowbite/controls/button.jinja" as Button %}
    {% import 'jinja_flowbite/controls/card.jinja' as Card %}
    
    <div class="flex flex-col items-center space-y-6">
        
        {{ Card.render(title="This is a card") }}
    
        {{ Button.render(text="Click me") }}
    
    <div>
    
  5. IMPORTANT - Rebuild the application's css file

    • For example:

      npx tailwindcss -i ./{{ FLASK_APP_DIR_NAME }}/static/css/app.css -o ./{{ FLASK_APP_DIR_NAME }}/static/css/app.min.css"
      

Create an Full Application Shell (Flask)

  1. Perform all the above getting started setup steps.

  2. Create a directory structure something like this:

    _ my_app/
      |_ static/
         |_ css
            |_ app.css               # contains tailwindcss instructions and your custom classes
            |_ app.min.css           # generated by tailwindcss npm tool
        |_ js
            |_ jinja-flowbite-app.js # will contain dark mode toggle logic 
      |_ templates/
        |_ components/
          |_ footer.jinja            # your custom footer content
          |_ sidebar.jinja           # your custom side bar content
        |_ layouts/
          |_ main_layout.html        # layout with header, sidebar, and footer
        |_ pages/
          |_ index.html              # template for the / route
          |_ index.py                # code for the / route
    
  3. In the jinja-flowbite-app.js, add the following code:

    // HANDLE DARK MODE ----------------------------------------------------------------------------
    
    var themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
    var themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
    
    // Change the icons inside the button based on previous settings
    if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && 
        window.matchMedia('(prefers-color-scheme: dark)').matches)) {
        themeToggleLightIcon.classList.remove('hidden');
    } else {
        themeToggleDarkIcon.classList.remove('hidden');
    }
    
    var themeToggleBtn = document.getElementById('theme-toggle');
    
    themeToggleBtn.addEventListener('click', function() {
    
        // toggle icons inside button
        themeToggleDarkIcon.classList.toggle('hidden');
        themeToggleLightIcon.classList.toggle('hidden');
    
        // if set via local storage previously
        if (localStorage.getItem('color-theme')) {
            if (localStorage.getItem('color-theme') === 'light') {
                document.documentElement.classList.add('dark');
                localStorage.setItem('color-theme', 'dark');
            } else {
                document.documentElement.classList.remove('dark');
                localStorage.setItem('color-theme', 'light');
            }
    
        // if NOT set via local storage previously
        } else {
            if (document.documentElement.classList.contains('dark')) {
                document.documentElement.classList.remove('dark');
                localStorage.setItem('color-theme', 'light');
            } else {
                document.documentElement.classList.add('dark');
                localStorage.setItem('color-theme', 'dark');
            }
        }
        
    });
    
  4. In the templates/layout/main_layout.html add the following content:

    {% import 'jinja_flowbite/icons/flowbite_logo.jinja' as flowbite_flowbite_icon %}
    {% import 'jinja_flowbite/controls/dark_mode_toggle.jinja' as flowbite_dark_mode_toggle %}
    
    {% extends "jinja_flowbite/layouts/main_layout.jinja" %}
    
    {% block flowbite_head_app_css_links_outlet %}
        <link href="{{url_for('static',filename='css/app.min.css')}}" rel="stylesheet" />
    {% endblock %}
    
    {% block flowbite_head_scripts_outlet %}
        <script src="{{url_for('static',filename='js/htmx.2.0.1.min.js')}}"></script>
    {% endblock %}
    
    
    {% block flowbite_body_scripts_outlet %}
        <script src="https://cdn.jsdelivr.net/npm/flowbite@2.4.1/dist/flowbite.min.js"></script>
        <script src="{{url_for('static',filename='js/jinja-flowbite-app.js')}}"></script>
    {% endblock %}
    
    
    {% block flowbite_header_content_left_outlet  %}
        <!-- Application Title -->
        <a class="flex items-center ml-2 sm:ml-0" data-enhance-nav="false">
            {{ flowbite_flowbite_icon.render(width_css_class="w-8", width_css_class="h-8", class="text-primary-500 mr-3" ) }}
            <a href="/" class="self-center text-2xl font-semibold whitespace-nowrap text-white"> Application Title </a>
        </a>
    {% endblock %}
    
    
    {% block flowbite_header_content_right_outlet  %}
        {{ flowbite_dark_mode_toggle.render(class="mr-1" ) }}
    {% endblock %}
    
    
    {% block flowbite_page_sidebar_outlet %}
        {% include "components/sidebar_content.jinja" %}
    {% endblock %}
    
    
    {% block flowbite_footer_content_outlet %}
        {% include "components/footer_content.jinja" %}
    {% endblock %}
    
    
    {% block flowbite_page_body_outlet %}
    
        {% block page_body %}{% endblock %}
    
    {% endblock %}
    
  5. In the templates/components/footer.jinja add the following content:

    <!-- left side footer -->
    <div role="footer-left-controls" class="flex items-center">
        <p> Left side text</p>
    </div>
    
    <!-- right side footer -->    
    <div>
        <div id="app-version" class="text-sm mr-2 flex space-x-2 items-center">
            <span>Application Title</span>
            <span>v0.1</span>
        </div>
        
    </div>
    
  6. In the templates/components/footer.jinja add the following content:

    <!-- left side footer -->
    <div role="footer-left-controls" class="flex items-center">
        <p> Left side text</p>
    </div>
    
    <!-- right side footer -->    
    <div>
        <div id="app-version" class="text-sm mr-2 flex space-x-2 items-center">
            <span>Application Title</span>
            <span>v0.1</span>
        </div>
        
    </div>
    
  7. In the templates/components/sidebar.jinja add the following content:

    {% set inactive_classes = "hover:bg-gray-100 dark:hover:bg-gray-700" %}
    {% set active_classes = "bg-primary-300 dark:bg-primary-800" %}
    
    <div class="py-5 px-3">
    
        <ul class="pt-5 mt-5 space-y-2 border-t border-gray-200 dark:border-gray-700">
    
            <!-- Home NavItem -->
            <li>
                {% set nav_active_classes = active_classes if 'home' in request.endpoint else inactive_classes %}
                <a href="/home"
                    class="flex items-center p-2 text-base font-medium text-gray-900 rounded-lg transition duration-75 dark:text-white group {{ nav_active_classes }}">
                    <svg aria-hidden="true"
                        class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
                        fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
                        <path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z"></path>
                        <path fill-rule="evenodd"
                            d="M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm3 4a1 1 0 000 2h.01a1 1 0 100-2H7zm3 0a1 1 0 000 2h3a1 1 0 100-2h-3zm-3 4a1 1 0 100 2h.01a1 1 0 100-2H7zm3 0a1 1 0 100 2h3a1 1 0 100-2h-3z"
                            clip-rule="evenodd"></path>
                    </svg>
                    <span class="ml-3">Home</span>
                </a>
            </li>
    
    
            <!-- Help NavItem -->
            <li>
                {% set nav_active_classes = active_classes if 'help' in request.endpoint else inactive_classes %}
                <a href="/help"
                    class="flex items-center p-2 text-base font-medium text-gray-900 rounded-lg transition duration-75 dark:text-white group {{ nav_active_classes }}">
                    <svg aria-hidden="true"
                        class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
                        fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
                        <path fill-rule="evenodd"
                            d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-2 0c0 .993-.241 1.929-.668 2.754l-1.524-1.525a3.997 3.997 0 00.078-2.183l1.562-1.562C15.802 8.249 16 9.1 16 10zm-5.165 3.913l1.58 1.58A5.98 5.98 0 0110 16a5.976 5.976 0 01-2.516-.552l1.562-1.562a4.006 4.006 0 001.789.027zm-4.677-2.796a4.002 4.002 0 01-.041-2.08l-.08.08-1.53-1.533A5.98 5.98 0 004 10c0 .954.223 1.856.619 2.657l1.54-1.54zm1.088-6.45A5.974 5.974 0 0110 4c.954 0 1.856.223 2.657.619l-1.54 1.54a4.002 4.002 0 00-2.346.033L7.246 4.668zM12 10a2 2 0 11-4 0 2 2 0 014 0z"
                            clip-rule="evenodd"></path>
                    </svg>
                    <span class="ml-3">Help</span>
                </a>
            </li>
        </ul>
    
    
    </div>
    
  8. In the templates/pages/home_page.html add the following content:

    {% extends "layouts/main_layout.html" %}
    {% block page_body %}
    
        <p class="text-3xl">Home</p>
        
    {% endblock %}
    
  9. In the templates/pages/home_page.py add the following content:

    from flask import render_template
    from my_app.webapp import app
    
    @app.route("/")
    def index_page():
        return render_template("pages/home_page.html",
                page_title="Home")
    
  10. In the templates/pages/help_page.html add the following content:

    {% extends "layouts/main_layout.html" %}
    {% block page_body %}
        <p class="text-3xl">Help</p>
    {% endblock %}
    
  11. In the templates/pages/help_page.py add the following content:

    from flask import render_template
    from my_app.webapp import app
    
    @app.route("/")
    def index_page():
        return render_template("pages/help_page.html",
                page_title="Help")
    
  12. Run the application and one should see the following:

    • custom header
    • custom footer
    • custom sidebar
      • the sidebar highlights the active page

END OF DOCUMENT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

jinja_flowbite-0.3.dev5-py3-none-any.whl (41.4 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page