Ok, so you really don't develop a custom site definition as a Feature in the WSSv3 sense of the word 'feature', but you can create a solution package which contains all the needed pieces for the custom site definition, including any Features you want activated on the site when it's first created.
While there's plenty of documentation around creating custom site definitions out there, I figured it would be beneficial to explain the steps needed to create the solution and deploy everything as a a single entity into WSSv3 as a WSP.
For this example, I'm going to create a custom version of the Team Site template without any significant changes because I don't want to deep dive into how to create a custom site definition. I'm then going to add a Label control to the default.aspx and populate it's value in a new code-code behind class for the default.aspx page that's not included out of the box.
While I don't like forcing an install any anyone, I'm going to use the Visual Studio 2005 project template I created which will take care of most of the tricky pieces that are required to create and deploy the solution This thing really does make custom solution development using Visual Studio for WSS a snap. Please download it and check it out.
Once you've installed the Visual Studio project template correctly, create a new project using the MOSS Solution Builder Project template. For this example, I named the project Example.CustomSiteSolution.
After the project has been created, you should sign the assembly using your own key file, but for the sake of this example, I'm going to leave the temp.snk file where it is.
Build the project. This will ensure that the assembly is in place so we can get the public key token using the sn.exe -T method. Once you have the public key token, replace the [Your_PKT] placeholder in the ManifestTemplate.xml file.
Navigate in Windows Explorer to \12\TEMPLATE\1033\XML. The xml files here contain the information needed by WSS to know what templates are available for sites to be created. Bill Baer has a good post on understanding the webtemp files if you want to know more about the specifics and the schema.
Since we're going to be creating a custom version of the Team Site template, copy the webtemp.xml file, and paste it into the \TEMPLATE\1033\XML directory of your VS project. Rename it to 'webtemp.customsitesolution.xml'. The new name doesn't matter too much other than that it has to start with webtemp, but it's a good idea to have it relate to the solution to which it's attached.
Open the xml file and delete all the Template elements except for the one with the Name="STS". Change the name to be CustomSiteExample, and change the ID to a number over 10000. Also delete the other Configuration elements except for the first one with the ID of 0. You can also change the name and description of the configuration element to be whatever you want.
The final version of the file should look like this:
<?xml version="1.0" encoding="utf-8"?>
<!-- _lcid="1033" _version="12.0.4518" _dal="1" -->
<!-- _LocalBinding -->
<Templates xmlns:ows="Microsoft SharePoint">
<Template Name="CustomSiteExample" ID="111000">
<Configuration ID="0" Title="Custom Site Exmaple Site" Hidden="FALSE"
ImageUrl="/_layouts/images/stsprev.png"
Description="An example site for learning to create a new site definition."
DisplayCategory="Collaboration" >
</Configuration>
</Template>
</Templates>
NOTE: Dang, I made a mistake in my original VS template. I had originally named the SiteTemplates directory 'SiteDefinitions'. If you've downloaded the VS template since 9/23, this has been fixed. If you got the template before then, renaming the SiteDefinitions folder to be SiteTemplates works, or you can download and install it again. Whoops...
Now navigate in Windows Explorer to \12\TEMPLATE\SiteTemplates. All of the folders in here are the site templates. Because we are creating a copy of the Team site template, copy the 'sts' folder - the Team Site definition - into the Template\SiteTemplates directory of the VS project and rename it to match the Name attribute value of the webtemp.customsitesolution.xml file, which I named CustomSiteExample, as shown above.
The directory structure of the project should now look like this:
Open the onet.xml file in TEMPLATE\SiteDefinitions\CustomSiteExample\xml of your VS project. This is the primary configuration file of the site definition. (I hear that onet stands for Office.NET, in case you're wondering.) To make this next part a little more manageable, collapse the primary nodes of the xml file so that it looks like this:
One of the first things I like to do in onet is add a new attribute to the Project element. Adding ' xmlns="http://schemas.microsoft.com/sharepoint/" ' to the element will give you Intellisence in the onet file - a huge help.
Change the /Project[@Title] value to be 'Custom Site Definition'
Expand the Configurations section and delete all of the Configuration nodes except for IDs -1 and 0
The /Project/Configurations/Configuration[@ID=0] node corresponds with the /Templates/Template[@ID=0] node we have in the webtemp.customsitesolution.xml file. If you want to create other custom site definitions that are similar to the one we're creating first, then all you need to do is create a new Configuration node with ID=1 in both the webtemp file and the onet file.
Next, expand the Modules element (/Project/Modules NOT /Project/Configuration[@ID=0]/Modules), and delete all of the Module elements except for the one where @Name=Default.
Essentially, that's it for creating the custom site definition. If you wanted to deploy the solution as it is right now, you'd have an exact replica of the Team Site template, with a different name. By building your Visual Studio project, having used the MOSS Solution Builder Project template, all the pieces are in the right place for creating a WSP file, and you should have a file named Exmapl.CustomSiteSolution.wsp in the /wsp directory of the project, as shown below.

Now, to create a code behind file for the default.aspx page that is included in the site definition.
In the CreateSiteExample folder in Visual Studio, create a new class file named default.aspx.cs. It should sit right next to default.aspx. Change the namespace to Example.CustomSiteSolution.CustomSiteExample. Change the name of the class from @default to _default.
Make the page inherit from WebPartPage by adding ' : WebPartPage' after the class name so that the file looks like this:
Here's a neat little trick: See the little red box beneath WebPartPage? That means that Visual Studio wants to help you out using a little love from the built-in Refactoring functionality. Whenever you see this, press Shift+Alt+F10. A little menu will open with the refactoring options available. In this case, because we have a reference to Microsoft.SharePoint, Visual Studio wants to add out using statement for us.
Now we need to add this namespace to the manifest.xml in the SafeControls section so the code will be allowed to run in the SharePoint context, so we need to add it to the SafeControls section of the ManifestTemplate.xml file, which will now look like this:
<SafeControls>
<SafeControl Safe="True"
Assembly="Example.CustomSiteSolution, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ef72d5cefb9f703d"
Namespace="Example.CustomSiteSolution" TypeName="*"/>
<SafeControl Safe="True"
Assembly="Example.CustomSiteSolution, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ef72d5cefb9f703d"
Namespace="Example.CustomSiteSolution.CustomSiteExample" TypeName="*"/>
</SafeControls>
When the project is built, the ManifestTemplate.xml is merged with auto-generated files to create the manifest.xml needed for the solution.
In the default.aspx, locate the Page directive at the top of the page. It should look like this:
<%@ Page language="C#" MasterPageFile="~masterurl/default.master" Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage,Microsoft.SharePoint,Version=12.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" %>
Change the Inherits attribute value to be "Example.CustomSiteSolution.CustomSiteExample._default." This ties the page to the new code behind file.
Place a asp:Label control named lblBreadCrumbHello right before the breadcrumb control so that it looks like this:
In the default.aspx.cs class, declare the Label control in the class like this:
This is similar to the way the .designer files work in the Asp.Net Web Application projects, but we have to add them by hand in WSS development. I wouldn't be surprised if WSS development in Visual Studio eventually works just like Asp.Net application projects, but until then, this isn't that bad.
Now, create a method to handle the Page_Load event in the class, and populate the asp:label control like this:
When you create a site, the breadcrumb should look like this:

That's it! Now let's deploy the solution...
To deploy, open both the deploy.cmd and upgrade.cmd files in the DeploymentScripts directory of your VS project. At the top, there's a line that looks like this:
Change the value of SITE_URL to be the URL of your site you want to deploy the solution to. Do the same for the upgrade.cmd file while you're at it.
To deploy the solution, navigate in Windows Explorer to the DeploymentScripts directory for the project. Double-click deploy.cmd to execute the deployment command. You might see an error the first time you deply saying that the solution is not in the solution store because the deployment script tries to delete the solution from the solution store first. This is needed because if you add new features to the solution, you needed to execute the deploysolution operation of stsadm in order for the features to be installed correctly.
If you are not adding new features, you can execute the upgrade.cmd file. This file updates the files on the server with the latest versions, as well as updating the assembly with any code changes. Chris O' Brian has a good post on adding features to site definitions.
After you deploy the solution for the first time, even though the ResetWebServer attribute in the manifest.xml is set to true, it's a good idea to run IISReset on the WFE servers to ensure that the new site definition is picked up. You shouldn't have to do this for subsequent deployments.
When you choose to create a new site, you should see the new site definition in the list of available templates:
There, a custom site definition deployed as a solution (using the Visual Studio Solution Project template) to all WFE servers running in your farm - one of the joys of solutions. Quick and easy, hopefully...