Skip to content
NovaSpark Docs v1.0.0
Live Demo Support

Maintain

Customization

Extend NovaSpark with your own CSS, PHP, template overrides and hooks.

Everything here is optional — NovaSpark is fully usable without opening a file. This page is for when you want to go past what the settings screens offer.

Two routes. Custom CSS / JS is update-safe on its own and needs no files. A child theme is for changing templates or adding PHP. Start with the first.

Custom CSS and JS #

At Theme Options → Custom CSS / JS. Three fields:

The Custom CSS / JS tab showing the code editor with syntax highlighting.
Theme Options → Custom CSS / JS. Rules here load after the theme's own styles.
Custom CSSLoaded on every page, after the theme’s own styles — so your rules win without !important.
Header ScriptsInjected into <head>. For analytics and verification tags.
Footer ScriptsInjected before </body>. Better for anything that does not need to run early.

Script fields run whatever you paste. Only paste code you understand or got from a service you trust. A broken tag here can stop every page rendering.

Design tokens #

Rather than fixed values, write CSS against NovaSpark’s custom properties. A token follows Theme Options → Design and adapts in dark mode; a hardcoded colour does neither.

Using tokens
.my-banner {
    background: rgb(var(--nk-primary));
    color: rgb(var(--nk-primary-contrast));
    border-radius: var(--nk-radius-brand);
    border: 1px solid var(--nk-border-color);
}
TokenIs
--nk-primary · --nk-secondary · --nk-accentYour brand colours. Each has a matching -contrast for readable text on top, and shades from -50 to -950.
--nk-heading-text · --nk-body-text · --nk-muted-textText colours.
--nk-surface · --nk-surface-raised · --nk-surface-overlayBackground levels.
--nk-border-colorThe standard border colour.
--nk-radius-brand · --nk-radius-lg · --nk-radius-fullCorner rounding.
--nk-section-spacing-sm-xlVertical rhythm between sections.
--nk-container-width · --nk-container-paddingSite width and gutters.
--nk-font-heading · -body · -secondary · -display · -monoThe five font roles.
--nk-shadow-sm · -md · -lgElevation.
--nk-transition-fast · -normal · -slowAnimation timings.

Colours are RGB channels, not colours. That is why they are wrapped in rgb() — it lets you add transparency with rgb(var(--nk-primary) / 50%).

Writing for dark mode

Dark mode adds .dark to the <html> element, and tokens change value underneath. Style with tokens and dark mode works by itself. When you do need something different:

Dark-mode override
.dark .my-banner { background: var(--nk-surface-raised); }

Template overrides #

Copy a file from the parent theme into your child theme, keeping the same path, and edit the copy. See Child theme for the files most people change.

Naming and prefixes #

You will see two prefixes in this theme’s code, and they are not interchangeable. Which one you use depends on who owns the thing you are hooking.

PrefixBelongs toUse it when
nk_ NovaKit Core — the engine plugin Hooking the engine: actions, filters, post types, meta keys. Works in any NovaKit-powered theme.
novaspark_ NovaSpark — this theme Calling or replacing one of the theme’s own template functions.

The split exists because NovaKit is a separate product. It powers this theme, but it is designed to power others too — so anything a second theme would also need carries the engine’s prefix, and anything specific to NovaSpark’s markup carries the theme’s.

A rule of thumb. If you are hooking something you’d expect to exist in a different NovaKit theme — the header slot, a Global Block type, the widget list — it is nk_. If it only makes sense because of how this theme draws a page, it is novaspark_.

novaspark-child/functions.php
// Engine hook — the same code works in any NovaKit theme.
add_action( 'nk_before_header', function (): void {
    echo '<div class="my-topbar">Free shipping this week</div>';
} );

// Theme function — specific to how NovaSpark renders a post.
if ( function_exists( 'novaspark_reading_time' ) ) {
    // Returns an int — minutes.
    echo esc_html( novaspark_reading_time() . ' min read' );
}

Never rename a nk_ identifier in the database. Post types like nk_global_section, meta keys like _nk_mm_enabled and the nk_ option keys are the storage layer. Changing them does not rename your content — it hides it, because the code goes looking under the old name and finds nothing.

CSS classes and data- attributes are a separate layer again and use the ns- prefix (.nk-header, data-nk-theme). Those are presentation only — safe to target from your own stylesheet, and never a place to store anything.

Hooks and filters #

Every hook below is an engine hook, so the same snippet works in any NovaKit-powered theme. Put them in your child theme’s functions.php.

Layout actions

Fixed points in the page where you can print your own markup. None of them take arguments.

ActionFires
nk_before_headerBefore the header. Announcement bars, overlays.
nk_headerWhere the header itself is drawn.
nk_after_headerJust after the header.
nk_page_coverWhere the page cover is drawn.
nk_before_footerBefore the footer.
nk_footerWhere the footer is drawn.
nk_after_footerAt the very end of the page.
novaspark-child/functions.php
// nk_before_header — an announcement bar above everything.
add_action( 'nk_before_header', function (): void {
    echo '<div class="my-topbar">Free shipping this week</div>';
} );

// nk_header — priority 5 runs before NovaKit draws its own header.
add_action( 'nk_header', 'my_custom_header', 5 );

// nk_after_header — the theme's own breadcrumbs, which echo directly.
add_action( 'nk_after_header', 'novaspark_breadcrumbs' );

// nk_page_cover — swap in your own cover for one post type.
add_action( 'nk_page_cover', function (): void {
    if ( is_singular( 'product' ) ) {
        get_template_part( 'template-parts/product-cover' );
    }
} );

// nk_before_footer — a call-to-action band above the footer.
add_action( 'nk_before_footer', function (): void {
    echo do_shortcode( '[my_newsletter_cta]' );
} );

// nk_footer — an extra row inside the footer, after NovaKit's.
add_action( 'nk_footer', 'my_extra_footer_row', 20 );

// nk_after_footer — the very last thing on the page.
add_action( 'nk_after_footer', function (): void {
    echo '<div id="my-cookie-bar"></div>';
} );

Event actions

These fire when something happens rather than at a point in the page, and most of them pass arguments — remember the 10, 2 on add_action() when you need the second one.

ActionArgumentsFires
nk_settings_updatedAfter Theme Options are saved or reset.
nk_account_registeredint $user_idAfter someone registers through a NovaKit Auth form.
nk_form_custom_submitarray $values, string $handleWhen a custom form is submitted.
nk_render_global_sectionint $section_id, string $slotAs a Global Block is drawn. $slot is header or footer.
nk_settings_updated
// Theme Options were saved — clear anything you derived from them.
add_action( 'nk_settings_updated', function (): void {
    delete_transient( 'my_nav_cache' );
} );
nk_account_registered
add_action( 'nk_account_registered', function ( int $user_id ): void {
    $user = get_userdata( $user_id );

    wp_mail(
        $user->user_email,
        'Welcome aboard',
        'Your account is ready.'
    );
} );
nk_form_custom_submit
add_action( 'nk_form_custom_submit', function ( array $values, string $handle ): void {
    if ( 'contact' !== $handle ) {
        return;
    }

    my_push_to_crm( $values[ 'email' ] ?? '' );
}, 10, 2 );
nk_render_global_section
add_action( 'nk_render_global_section', function ( int $section_id, string $slot ): void {
    if ( 'footer' === $slot ) {
        my_track_block( $section_id );
    }
}, 10, 2 );

Filters

Each one receives a value and must return it — forgetting the return is the most common way to blank out a list by accident.

FilterChanges
nk_elementor_widget_enabledWhether a widget is registered. Return false to remove one.
nk_section_typesThe list of Global Block types.
nk_design_presetsThe design presets offered in Theme Options.
nk_dashboard_recommended_pluginsThe plugin list on the dashboard.
nk_demo_sourcesWhere demos are fetched from.
nk_library_sourcesWhere NovaKit Library templates come from.
nk_smart_loops_card_stylesThe card styles Smart Loops offers.
nk_testimonial_skinsThe testimonial skins available.
nk_testimonial_containersHow testimonials are arranged (grid, masonry, carousel, marquee).
nk_icon_sets_to_loadWhich icon sets load on the front end.
nk_custom_header_argsThe custom-header theme support arguments.
nk_archive_card_template_idWhich Card Global Block renders a given post.
nk_elementor_widget_enabled
add_filter( 'nk_elementor_widget_enabled', function ( bool $enabled, string $name ): bool {
    // Widget names are the get_name() value, e.g. 'nk-brands'.
    return 'nk-brands' === $name ? false : $enabled;
}, 10, 2 );
nk_section_types
add_filter( 'nk_section_types', function ( array $types ): array {
    // slug => label. Adds a new type to the Global Blocks taxonomy.
    $types[ 'promo' ] = __( 'Promo Bar', 'my-child' );

    return $types;
} );
nk_design_presets
add_filter( 'nk_design_presets', function ( array $presets ): array {
    // Hide a bundled preset from Theme Options.
    unset( $presets[ 'finance' ] );

    return $presets;
} );
nk_dashboard_recommended_plugins
add_filter( 'nk_dashboard_recommended_plugins', function ( array $plugins ): array {
    return array_values( array_filter(
        $plugins,
        fn( array $p ): bool => 'jetpack' !== $p[ 'slug' ]
    ) );
} );
nk_demo_sources
add_filter( 'nk_demo_sources', function ( array $sources ): array {
    // Must implement DemoImporter\Sources\DemoSourceInterface.
    $sources[] = new My_Demo_Source();

    return $sources;
} );
nk_library_sources
add_filter( 'nk_library_sources', function ( array $sources ): array {
    // Must implement Library\Sources\SourceInterface.
    $sources[] = new My_Library_Source();

    return $sources;
} );
nk_smart_loops_card_styles
add_filter( 'nk_smart_loops_card_styles', function ( array $styles ): array {
    // slug => [ 'label' => …, 'render' => callable ]
    unset( $styles[ 'custom-template' ] );

    return $styles;
} );
nk_testimonial_skins
add_filter( 'nk_testimonial_skins', function ( array $skins ): array {
    unset( $skins[ 'custom-template' ] );

    return $skins;
} );
nk_testimonial_containers
add_filter( 'nk_testimonial_containers', function ( array $containers ): array {
    // slug => [ 'label' => …, 'engine' => … ]
    unset( $containers[ 'marquee' ] );

    return $containers;
} );
nk_icon_sets_to_load
add_filter( 'nk_icon_sets_to_load', function ( array $used, array $enabled ): array {
    // $used is what NovaKit detected on the page; $enabled is every set
    // switched on. Return $enabled to stop relying on detection.
    return $enabled;
}, 10, 2 );
nk_custom_header_args
add_filter( 'nk_custom_header_args', function ( array $args ): array {
    $args[ 'width' ]  = 2560;
    $args[ 'height' ] = 600;

    return $args;
} );
nk_archive_card_template_id
add_filter( 'nk_archive_card_template_id', function ( int $id, WP_Post $post ): int {
    // Use a different Card Global Block for one post type.
    return 'product' === $post->post_type ? 1234 : $id;
}, 10, 2 );

Alpine and GSAP in brief #

NovaSpark uses two small libraries on the front end, with a clean split: Alpine.js handles interactive state — what is open, which tab is active — and GSAP handles movement.

Both are already loaded, so a child theme can use them without adding anything. Alpine components are attached with x-data in the markup; NovaSpark registers its own on alpine:init, so register yours the same way if you add any.

You will not usually need either. Motion is available through NovaKit Motion without code, and the interactive widgets bring their own behaviour. These are here for the cases the settings do not cover.

Esc