Back

Back

Back

Simple Documentation Maker

Simple Documentation Maker

Page Contents

Page Contents

Page Contents

Need a simple documentation system? This component adds auto linking contents list to a one page document system.
Need a simple documentation system? This component adds auto linking contents list to a one page document system.
Need a simple documentation system? This component adds auto linking contents list to a one page document system.

Need a simple documentation system? This component adds auto linking contents list to a one page document system.

Looking to add a bit of documentation to your project? This tool is perfect for you!

Have you ever been asked to include documentation tools to enhance your builds? If so, you might have already explored the power of the Framer CMS.

This component enables you to add page titles in a sidebar that automatically link to articles in the main page content. It’s ideal for smaller documentation libraries, where you want all your information on a single page, organized in a vertical stack.

With full customization options, this component can be easily tailored to match your brand. It’s simple to set up—just grab the remix file to see how the implementation works!

If you are not sure how to add code to Framer watch this quick video.

Framer Code Component

Simple Documentation Maker

1.0

1.0

1.0

1.0

// A custom Framer code override by Chris Kellett - Framerverse
// Get more components at www.framerverse.com
// Simple Docs Sidelinks
// Version 1.0

import React, { useState, useEffect, useRef } from "react";
import { Frame, addPropertyControls, ControlType } from "framer";

export function SidebarLink(props) {
    const {
        text,
        link,
        activeColor,
        inactiveColor,
        hoverColor,
        font,
        fontSize,
        fontWeight,
    } = props;
    const [isActive, setIsActive] = useState(false);
    const observer = useRef(null);

    useEffect(() => {
        // Function to update active state
        const updateActiveState = () => {
            const hash = window.location.hash.slice(1);
            setIsActive(hash === link);
        };

        // Set up Intersection Observer
        observer.current = new IntersectionObserver(
            (entries) => {
                entries.forEach((entry) => {
                    if (entry.isIntersecting) {
                        setIsActive(true);
                        window.history.pushState(null, null, `#${link}`);
                    } else {
                        setIsActive(false);
                    }
                });
            },
            { threshold: 0.5 } // Trigger when 50% of the target is visible
        );

        // Observe the corresponding section
        const targetSection = document.getElementById(link);
        if (targetSection) {
            observer.current.observe(targetSection);
        }

        // Listen for hash changes
        window.addEventListener("hashchange", updateActiveState);
        updateActiveState(); // Initial check

        // Cleanup
        return () => {
            window.removeEventListener("hashchange", updateActiveState);
            if (observer.current && targetSection) {
                observer.current.unobserve(targetSection);
            }
        };
    }, [link]);

    return (
        <div
            style={{
                width: "100%",
                height: "auto",
                textAlign: "left",
                display: "flex",
                overflow: "hidden", // Prevent scrollbar
            }}
        >
            <div
                background="none"
                width="100%"
                style={{
                    color: isActive ? activeColor : inactiveColor,
                    fontFamily: font,
                    fontSize: fontSize,
                    fontWeight: fontWeight,
                    textDecoration: "none",
                    cursor: "pointer",
                    textAlign: "left",
                    display: "flex",
                    alignItems: "center", // Ensure vertical centering
                    transition: "color 0.3s ease, transform 0.3s ease",
                    transform: isActive ? "translateX(8px)" : "translateX(0px)",
                    whiteSpace: "normal", // Allow text to wrap to the next line
                }}
                whileHover={{ color: hoverColor }}
                onClick={() => {
                    const element = document.getElementById(link);
                    if (element) {
                        element.scrollIntoView({ behavior: "smooth" });
                    }
                }}
            >
                {isActive && (
                    <span
                        style={{
                            marginRight: "8px",
                            transition: "opacity 0.3s ease",
                            opacity: isActive ? 1 : 0,
                        }}
                    ></span>
                )}
                <span>{text}</span>
            </div>
        </div>
    );
}

SidebarLink.defaultProps = {
    text: "Link Text",
    link: "page-1",
    activeColor: "#27AB8A",
    inactiveColor: "white",
    hoverColor: "#27AB8A",
    font: "Inter",
    fontSize: 16,
    fontWeight: 400, // Default font weight
};

addPropertyControls(SidebarLink, {
    text: { type: ControlType.String, title: "Text" },
    link: { type: ControlType.String, title: "Link (without #)" },
    activeColor: { type: ControlType.Color, title: "Active Color" },
    inactiveColor: { type: ControlType.Color, title: "Inactive Color" },
    hoverColor: { type: ControlType.Color, title: "Hover Color" },
    font: { type: ControlType.String, title: "Font" },
    fontSize: {
        type: ControlType.Number,
        title: "Font Size",
        min: 8,
        max: 72,
        step: 1,
    },
    fontWeight: {
        type: ControlType.Number,
        title: "Font Weight",
        min: 100,
        max: 900,
        step: 100,
    },
});

CLICK TO COPY

// A custom Framer code override by Chris Kellett - Framerverse
// Get more components at www.framerverse.com
// Simple Docs Sidelinks
// Version 1.0

import React, { useState, useEffect, useRef } from "react";
import { Frame, addPropertyControls, ControlType } from "framer";

export function SidebarLink(props) {
    const {
        text,
        link,
        activeColor,
        inactiveColor,
        hoverColor,
        font,
        fontSize,
        fontWeight,
    } = props;
    const [isActive, setIsActive] = useState(false);
    const observer = useRef(null);

    useEffect(() => {
        // Function to update active state
        const updateActiveState = () => {
            const hash = window.location.hash.slice(1);
            setIsActive(hash === link);
        };

        // Set up Intersection Observer
        observer.current = new IntersectionObserver(
            (entries) => {
                entries.forEach((entry) => {
                    if (entry.isIntersecting) {
                        setIsActive(true);
                        window.history.pushState(null, null, `#${link}`);
                    } else {
                        setIsActive(false);
                    }
                });
            },
            { threshold: 0.5 } // Trigger when 50% of the target is visible
        );

        // Observe the corresponding section
        const targetSection = document.getElementById(link);
        if (targetSection) {
            observer.current.observe(targetSection);
        }

        // Listen for hash changes
        window.addEventListener("hashchange", updateActiveState);
        updateActiveState(); // Initial check

        // Cleanup
        return () => {
            window.removeEventListener("hashchange", updateActiveState);
            if (observer.current && targetSection) {
                observer.current.unobserve(targetSection);
            }
        };
    }, [link]);

    return (
        <div
            style={{
                width: "100%",
                height: "auto",
                textAlign: "left",
                display: "flex",
                overflow: "hidden", // Prevent scrollbar
            }}
        >
            <div
                background="none"
                width="100%"
                style={{
                    color: isActive ? activeColor : inactiveColor,
                    fontFamily: font,
                    fontSize: fontSize,
                    fontWeight: fontWeight,
                    textDecoration: "none",
                    cursor: "pointer",
                    textAlign: "left",
                    display: "flex",
                    alignItems: "center", // Ensure vertical centering
                    transition: "color 0.3s ease, transform 0.3s ease",
                    transform: isActive ? "translateX(8px)" : "translateX(0px)",
                    whiteSpace: "normal", // Allow text to wrap to the next line
                }}
                whileHover={{ color: hoverColor }}
                onClick={() => {
                    const element = document.getElementById(link);
                    if (element) {
                        element.scrollIntoView({ behavior: "smooth" });
                    }
                }}
            >
                {isActive && (
                    <span
                        style={{
                            marginRight: "8px",
                            transition: "opacity 0.3s ease",
                            opacity: isActive ? 1 : 0,
                        }}
                    ></span>
                )}
                <span>{text}</span>
            </div>
        </div>
    );
}

SidebarLink.defaultProps = {
    text: "Link Text",
    link: "page-1",
    activeColor: "#27AB8A",
    inactiveColor: "white",
    hoverColor: "#27AB8A",
    font: "Inter",
    fontSize: 16,
    fontWeight: 400, // Default font weight
};

addPropertyControls(SidebarLink, {
    text: { type: ControlType.String, title: "Text" },
    link: { type: ControlType.String, title: "Link (without #)" },
    activeColor: { type: ControlType.Color, title: "Active Color" },
    inactiveColor: { type: ControlType.Color, title: "Inactive Color" },
    hoverColor: { type: ControlType.Color, title: "Hover Color" },
    font: { type: ControlType.String, title: "Font" },
    fontSize: {
        type: ControlType.Number,
        title: "Font Size",
        min: 8,
        max: 72,
        step: 1,
    },
    fontWeight: {
        type: ControlType.Number,
        title: "Font Weight",
        min: 100,
        max: 900,
        step: 100,
    },
});

CLICK TO COPY

// A custom Framer code override by Chris Kellett - Framerverse
// Get more components at www.framerverse.com
// Simple Docs Sidelinks
// Version 1.0

import React, { useState, useEffect, useRef } from "react";
import { Frame, addPropertyControls, ControlType } from "framer";

export function SidebarLink(props) {
    const {
        text,
        link,
        activeColor,
        inactiveColor,
        hoverColor,
        font,
        fontSize,
        fontWeight,
    } = props;
    const [isActive, setIsActive] = useState(false);
    const observer = useRef(null);

    useEffect(() => {
        // Function to update active state
        const updateActiveState = () => {
            const hash = window.location.hash.slice(1);
            setIsActive(hash === link);
        };

        // Set up Intersection Observer
        observer.current = new IntersectionObserver(
            (entries) => {
                entries.forEach((entry) => {
                    if (entry.isIntersecting) {
                        setIsActive(true);
                        window.history.pushState(null, null, `#${link}`);
                    } else {
                        setIsActive(false);
                    }
                });
            },
            { threshold: 0.5 } // Trigger when 50% of the target is visible
        );

        // Observe the corresponding section
        const targetSection = document.getElementById(link);
        if (targetSection) {
            observer.current.observe(targetSection);
        }

        // Listen for hash changes
        window.addEventListener("hashchange", updateActiveState);
        updateActiveState(); // Initial check

        // Cleanup
        return () => {
            window.removeEventListener("hashchange", updateActiveState);
            if (observer.current && targetSection) {
                observer.current.unobserve(targetSection);
            }
        };
    }, [link]);

    return (
        <div
            style={{
                width: "100%",
                height: "auto",
                textAlign: "left",
                display: "flex",
                overflow: "hidden", // Prevent scrollbar
            }}
        >
            <div
                background="none"
                width="100%"
                style={{
                    color: isActive ? activeColor : inactiveColor,
                    fontFamily: font,
                    fontSize: fontSize,
                    fontWeight: fontWeight,
                    textDecoration: "none",
                    cursor: "pointer",
                    textAlign: "left",
                    display: "flex",
                    alignItems: "center", // Ensure vertical centering
                    transition: "color 0.3s ease, transform 0.3s ease",
                    transform: isActive ? "translateX(8px)" : "translateX(0px)",
                    whiteSpace: "normal", // Allow text to wrap to the next line
                }}
                whileHover={{ color: hoverColor }}
                onClick={() => {
                    const element = document.getElementById(link);
                    if (element) {
                        element.scrollIntoView({ behavior: "smooth" });
                    }
                }}
            >
                {isActive && (
                    <span
                        style={{
                            marginRight: "8px",
                            transition: "opacity 0.3s ease",
                            opacity: isActive ? 1 : 0,
                        }}
                    ></span>
                )}
                <span>{text}</span>
            </div>
        </div>
    );
}

SidebarLink.defaultProps = {
    text: "Link Text",
    link: "page-1",
    activeColor: "#27AB8A",
    inactiveColor: "white",
    hoverColor: "#27AB8A",
    font: "Inter",
    fontSize: 16,
    fontWeight: 400, // Default font weight
};

addPropertyControls(SidebarLink, {
    text: { type: ControlType.String, title: "Text" },
    link: { type: ControlType.String, title: "Link (without #)" },
    activeColor: { type: ControlType.Color, title: "Active Color" },
    inactiveColor: { type: ControlType.Color, title: "Inactive Color" },
    hoverColor: { type: ControlType.Color, title: "Hover Color" },
    font: { type: ControlType.String, title: "Font" },
    fontSize: {
        type: ControlType.Number,
        title: "Font Size",
        min: 8,
        max: 72,
        step: 1,
    },
    fontWeight: {
        type: ControlType.Number,
        title: "Font Weight",
        min: 100,
        max: 900,
        step: 100,
    },
});

CLICK TO COPY

How to use.

Click in the code are below to copy the code to your clipboard. Then click the 'plus' button int the assets panel for 'Code'. Choose if its an overide or Component. Paste the code in and save.

How to use.

Click in the code are below to copy the code to your clipboard. Then click the 'plus' button int the assets panel for 'Code'. Choose if its an overide or Component. Paste the code in and save.

How to use.

Click in the code are below to copy the code to your clipboard. Then click the 'plus' button int the assets panel for 'Code'. Choose if its an overide or Component. Paste the code in and save.

Change Log

// Version 1.0

Figma to Framer

Support

If you need support first watch the help video above. If that does not help reach out to me on one of my social channels or use the contact form. As I am a team of one it may take a short while to get back to you. Please be patient, thanks.

Hope this helps. Good luck with your project.

More Framer resources.

Add a fully functional, responsive, configurable loan calculator to your project.

Real Loan Calculator

Add a fully functional, responsive, configurable loan calculator to your project.

Section

Add a fully functional, responsive, configurable loan calculator to your project.

Real Loan Calculator

Add a fully functional, responsive, configurable loan calculator to your project.

Section

Add a fully functional, responsive, configurable loan calculator to your project.

Real Loan Calculator

Add a fully functional, responsive, configurable loan calculator to your project.

Section

If your site works better in one orientation than another why not let your users know with this little component.

Mobile Rotation Alert

If your site works better in one orientation than another why not let your users know with this little component.

Code Component

If your site works better in one orientation than another why not let your users know with this little component.

Mobile Rotation Alert

If your site works better in one orientation than another why not let your users know with this little component.

Code Component

If your site works better in one orientation than another why not let your users know with this little component.

Mobile Rotation Alert

If your site works better in one orientation than another why not let your users know with this little component.

Code Component

A niche component for creating a swirling Starfield, simple but great in the right scenario (like this site).

Starfield Generator

A niche component for creating a swirling Starfield, simple but great in the right scenario (like this site).

Code Component

A niche component for creating a swirling Starfield, simple but great in the right scenario (like this site).

Starfield Generator

A niche component for creating a swirling Starfield, simple but great in the right scenario (like this site).

Code Component

A niche component for creating a swirling Starfield, simple but great in the right scenario (like this site).

Starfield Generator

A niche component for creating a swirling Starfield, simple but great in the right scenario (like this site).

Code Component

Use this component and overide combo to create a swipe toggle that switches component variants.

Swipe Variant Changer

Use this component and overide combo to create a swipe toggle that switches component variants.

Code Override

Use this component and overide combo to create a swipe toggle that switches component variants.

Swipe Variant Changer

Use this component and overide combo to create a swipe toggle that switches component variants.

Code Override

Use this component and overide combo to create a swipe toggle that switches component variants.

Swipe Variant Changer

Use this component and overide combo to create a swipe toggle that switches component variants.

Code Override

Use a URL parameter to change the variant on the page you link to.

Change Variants with URL Parameters

Use a URL parameter to change the variant on the page you link to.

Code Override

Use a URL parameter to change the variant on the page you link to.

Change Variants with URL Parameters

Use a URL parameter to change the variant on the page you link to.

Code Override

Use a URL parameter to change the variant on the page you link to.

Change Variants with URL Parameters

Use a URL parameter to change the variant on the page you link to.

Code Override

Build simple form experiences that look, feel and work like a multi-step form.

Multi-step Form Experience

Build simple form experiences that look, feel and work like a multi-step form.

Section

Build simple form experiences that look, feel and work like a multi-step form.

Multi-step Form Experience

Build simple form experiences that look, feel and work like a multi-step form.

Section

Build simple form experiences that look, feel and work like a multi-step form.

Multi-step Form Experience

Build simple form experiences that look, feel and work like a multi-step form.

Section